標籤:調用 __name__ 服務 列表 process 工作 傳遞 sleep 守護
本文是學習《Python核心編程》的學習筆記,介紹了Python中的全域解譯器鎖和常用的兩個線程模組:thread, threading,並對比他們的優缺點和給出簡單的列子。
全域解譯器鎖(GIL)
Python代碼的執行都是有Python虛擬機器進行控制的。當初設計Python的時候,考慮在主迴圈中只能有一個控制線程在執行,就像單核CPU進行多線程編程一樣。
怎麼做到這樣控制的呢?就是這裡的GIL來控制的,這個鎖用來保證同時只有一個線程在運行。
執行方式:
這幾個細節知識點:
- 當調用外部代碼(C/C++擴充的內建函數)時, GIL會保持鎖定,知道函數執行結束
- 對於面向I/O的Python常式,GIL會在I/O調用前被釋放,從而允許其他線程在I/O執行期間運行
- 如果是針對計算密集型的作業碼,該線程整個時間片內更傾向於始終佔有處理器和GIL。
所以,針對Python虛擬機器單線程(GIL)的設計原因,只有線程在執行I/O密集型的應用,才能更好的發揮Python的並發性。
對比thread,threading
Python多個模組可以進行多線程編程,包括:thread,threading等。他們都可以用來建立和管理線程。
thread模組提供了基本的線程和鎖定支援,而threading模組提供了更進階別,功能更全面的線程管理。
推薦使用更進階別的threading模組,下面是一個簡單的對比:
| 特徵要素 |
thread |
threading |
| 功能全面性 |
基本(偏底層) |
全面,進階 |
| 守護進程 |
不支援 |
支援 |
| 線程同步原語 |
僅1個(Lock) |
很多(Lock,Semaphore,Barrier...) |
守護進程講解
守護進程一般是一個等待用戶端請求服務的伺服器。如果沒有用戶端請求,守護進程一般是閒置。一般把一個線程設定成守護進程,就表示這個線程是不重要的。所以進程退出時是不需要等待這個守護線程完成的。
但是原先的thread 模組是不區分守護或者非守護進程的,也就是說當主線程退出的時候,所有子線程都將終止,而不管他們是否仍在工作。如果你不想這種情況發生,那麼就可以採用threading模組。整個Python程式(一般為主線程)將在所有非守護進程退出是才會退出。
設定守護進程,線上程啟動之前設定:
thread.daemon = True
多線程實踐threading執行個體
方式1:建立一個thread執行個體,傳遞它一個函數
import threadingfrom time import sleep, ctimesleep_times = [4, 2]def loop(threadNo, sleep_time): print(‘Start loop‘, threadNo, ‘at:‘, ctime()) sleep(sleep_time) #Sleep一段時間 print(‘loop‘, threadNo, ‘done at:‘, ctime())def main(): print(‘starting at:‘, ctime()) threads = [] threadIds = range(len(sleep_times)) for i in threadIds: thread = threading.Thread(target=loop, args=(i,sleep_times[i])) threads.append(thread) for t in threads: # 依次啟動線程 t.start() for t in threads: # 等待所有線程完成 t.join() #將等待線程結束 print(‘all Done at :‘, ctime())if __name__ == ‘__main__‘: main()
方式2:派生Thread的子類,並建立子類的執行個體
import threadingfrom time import sleep, ctimesleep_times = [4, 2]class MyThread(threading.Thread): def __init__(self, func, args, name=‘‘): threading.Thread.__init__(self) self.func = func self.name = name self.args = args def run(self): self.func(*self.args)def loop(threadNo, sleep_time): print(‘Start loop‘, threadNo, ‘at:‘, ctime()) sleep(sleep_time) # Sleep一段時間 print(‘loop‘, threadNo, ‘done at:‘, ctime())def main(): print(‘starting at:‘, ctime()) threads = [] # 用於儲存所有線程執行個體的列表 threadIds = range(len(sleep_times)) for i in threadIds: # 建立線程執行個體 thread = MyThread(loop, (i, sleep_times[i])) threads.append(thread) for t in threads: # 依次啟動線程 t.start() for t in threads: # 等待所有線程完成 t.join() # 將等待線程結束 print(‘all Done at :‘, ctime())if __name__ == ‘__main__‘: main()
thread執行個體
由於本人使用的python3.6,這個thread已經變成_thread
import _threadfrom time import sleep, ctimesleep_times = [4, 2]def loop(threadNo, sleep_time, lock): print(‘Start loop‘, threadNo, ‘at:‘, ctime()) sleep(sleep_time) #Sleep一段時間 print(‘loop‘, threadNo, ‘done at:‘, ctime()) lock.release() #釋放鎖def main(): print(‘starting at:‘, ctime()) locks = [] threadIds = range(len(sleep_times)) for i in threadIds: #通過調用_thread.allocate_lock獲得鎖對象 lock = _thread.allocate_lock() #通過acquire()方法取得鎖 lock.acquire() locks.append(lock) for i in threadIds: # 依次啟動線程 _thread.start_new_thread(loop, (i, sleep_times[i], locks[i])) for i in threadIds: # 如果當前的鎖Lock沒有釋放的話,一直迴圈等待 while locks[i].locked(): pass print(‘all Done at :‘, ctime())if __name__ == ‘__main__‘: main()
Python 3多線程編程學習筆記-基礎篇