桐花万里python路-高级篇-并发编程-03-线程
生活随笔
收集整理的這篇文章主要介紹了
桐花万里python路-高级篇-并发编程-03-线程
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
- 理論
- 進(jìn)程只是一個資源單位,線程才是cpu上的執(zhí)行單位
- 無需申請空間,創(chuàng)建開銷小
- 共享和創(chuàng)建開銷
- 多線程共享一個進(jìn)程的地址空間
- 線程比進(jìn)程更輕量級,線程比進(jìn)程更容易創(chuàng)建可撤銷
- I/O密集型,多線程,會加快程序執(zhí)行的速度
- 在多cpu系統(tǒng)中,為了最大限度的利用多核,可以開啟多個線程,比開進(jìn)程開銷要小的多。(這一條并不適用于python)
- 線程操作
- 創(chuàng)建方式
- from threading import Thread
- t=Thread(target=func,args=('hello',)) ; t.start()
- 繼承Thread類,實現(xiàn)run()方法
- 其他方法
- t.isAlive(): 返回線程是否活動的
- t.getName(): 返回線程名
- t.setName(): 設(shè)置線程名
- threading.currentThread(): 返回當(dāng)前的線程變量
- threading.enumerate(): 返回一個包含正在運行的線程的list
- threading.activeCount(): 返回正在運行的線程數(shù)量,與len(threading.enumerate())有相同的結(jié)果
- 線程間關(guān)系
- 串行 join
- 主線程等待子線程結(jié)束?
- 主線程所在的進(jìn)程內(nèi)所有非守護(hù)線程統(tǒng)統(tǒng)運行完畢,主線程才算運行完畢
- 守護(hù)線程
- t.daemon = True
- 無論是進(jìn)程還是線程,都遵循:守護(hù)xxx會等待主xxx運行完畢后被銷毀 1.對主進(jìn)程來說,運行完畢指的是主進(jìn)程代碼運行完畢 2.對主線程來說,運行完畢指的是主線程所在的進(jìn)程內(nèi)所有非守護(hù)線程統(tǒng)統(tǒng)運行完畢,主線程才算運行完畢
- 串行 join
- 創(chuàng)建方式
- 線程同步
- Python的GIL
- 在Cpython解釋器中,同一個進(jìn)程下開啟的多線程,同一時刻只能有一個線程執(zhí)行,無法利用多核優(yōu)勢
- GIL本質(zhì)就是一把互斥鎖,保護(hù)不同的數(shù)據(jù)的安全,就應(yīng)該加不同的鎖。 將并發(fā)運行變成串行,以此來控制同一時間內(nèi)共享數(shù)據(jù)只能被一個任務(wù)所修改,進(jìn)而保證數(shù)據(jù)安全
- 在一個python的進(jìn)程內(nèi),不僅有主線程或者由該主線程開啟的其他線程,還有解釋器開啟的垃圾回收等解釋器級別的線程
- 多個線程的target=work執(zhí)行流程
- 多個線程先訪問到解釋器的代碼,即拿到執(zhí)行權(quán)限
- 所有數(shù)據(jù)都是共享的,代碼作為一種數(shù)據(jù)也是被所有線程共享的
- 所有線程的任務(wù),都需要將任務(wù)的代碼當(dāng)做參數(shù)傳給解釋器的代碼去執(zhí)行
- 將target的代碼交給解釋器的代碼去執(zhí)行
- 多個線程先訪問到解釋器的代碼,即拿到執(zhí)行權(quán)限
- 對計算來說,cpu越多越好,但是對于I/O來說,再多的cpu也沒用
- IT(IO-Thread)多線程用于IO密集型,如socket,爬蟲,web
- CP(Calculate-Process)多進(jìn)程用于計算密集型,如金融分析
- 對于同一個數(shù)據(jù)100,可能線程1執(zhí)行x=100的同時,而垃圾回收執(zhí)行的是回收100的操作,解決這種問題沒有什么高明的方法,就是加鎖處理,如下圖的GIL,保證python解釋器同一時間只能執(zhí)行一個任務(wù)的代碼
- 遞歸鎖 RLock
- 死鎖: 兩個或兩個以上的進(jìn)程或線程在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現(xiàn)象
- 在同一線程中多次請求同一資源,python提供了可重入鎖RLock
- 內(nèi)部維護(hù)著一個Lock和一個counter變量
- Counter記錄了acquire的次數(shù),從而使得資源可以被多次require
- 直到一個線程所有的acquire都被release,其他的線程才能獲得資源
- 信號量 Semaphore
- 管理一個內(nèi)置的計數(shù)器
- from threading import Thread,Semaphore
- 預(yù)置量 sm=Semaphore(5)
- 鎖定 sm.acquire()
- 釋放?sm.release()
- 會產(chǎn)生新的線程
- 事件 Event
- 通過判斷某個線程的狀態(tài),本質(zhì)是修改全局變量
- 由線程設(shè)置的信號標(biāo)志,它允許線程等待某些事件的發(fā)生
- 初始情況下,Event對象中的信號標(biāo)志被設(shè)置為假
- 有線程等待一個Event對象, 而這個Event對象的標(biāo)志為假,那么這個線程將會被一直阻塞直至該標(biāo)志為真
- 一個線程如果將一個Event對象的信號標(biāo)志設(shè)置為真,它將喚醒所有等待這個Event對象的線程
- 如果一個線程等待一個已經(jīng)被設(shè)置為真的Event對象,那么它將忽略這個事件, 繼續(xù)執(zhí)行
- 方法
- event.isSet():返回event的狀態(tài)值
- event.wait(timeout=3):如果 event.isSet()==False將阻塞線程,超時時間timeout
- event.set(): 設(shè)置event的狀態(tài)值為True,所有阻塞池的線程激活進(jìn)入就緒狀態(tài), 等待操作系統(tǒng)調(diào)度
- event.clear():恢復(fù)event的狀態(tài)值為False
- 定時器
- 指定n秒后執(zhí)行某操作
- from threading import Timer
- t = Timer(3, hello)
- t.start()
- Python的GIL
- 線程池
- 線程queue
- 使用import queue,用法與進(jìn)程Queue一樣
- q=queue.Queue() 先進(jìn)先出
- queue.LifoQueue(maxsize=0)? 后進(jìn)先出
- queue.PriorityQueue(maxsize=0) 存儲數(shù)據(jù)時可設(shè)置優(yōu)先級的隊列
- q.put((20,'a'))
- 元組的第一個元素是優(yōu)先級(通常是數(shù)字,也可以是非數(shù)字之間的比較),數(shù)字越小優(yōu)先級越高
- concurrent.futures
- 高度封裝的異步調(diào)用接口
- 線程池?ThreadPoolExecutor
- max_workers :cpu_count*5
- 進(jìn)程池?ProcessPoolExecutor
- max_workers :cpu_count
- 方法
- submit(fn, *args, **kwargs)
- map(func, *iterables, timeout=None, chunksize=1)?異步,須在shutdown之前
- 循環(huán)submit
- executor.map(task,range(1,12))? map取代了for+submit
- shutdown(wait=True)
- 相當(dāng)于進(jìn)程池的pool.close()+pool.join()操作
- wait=True,等待池內(nèi)所有任務(wù)執(zhí)行完畢回收完資源后才繼續(xù)
- wait=False,立即返回,并不會等待池內(nèi)的任務(wù)執(zhí)行完畢
- result(timeout=None) 取得結(jié)果
- add_done_callback(fn) 回調(diào)函數(shù)
- p.submit(get_page,url).add_done_callback(parse_page)
- parse_page拿到的是一個future對象obj,需要用obj.result()拿到結(jié)果 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutorimport os,time,random
def task(n):print('%s is runing' %os.getpid())time.sleep(random.randint(1,3))return n**2if __name__ == '__main__':executor=ThreadPoolExecutor(max_workers=3)# for i in range(11):# future=executor.submit(task,i)
executor.map(task,range(1,12)) #map取代了for+submit
?
- 線程queue
轉(zhuǎn)載于:https://www.cnblogs.com/zhujingxiu/p/8601953.html
總結(jié)
以上是生活随笔為你收集整理的桐花万里python路-高级篇-并发编程-03-线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Mapping实现的以太坊智能合约的
- 下一篇: python---memcache使用操