1.同時執行多個任務
- 多進程模式
- 多線程模式
- 多進程+多線程
注:進程是操作系統分配資源的最小單元。線程是操作系統調度的最小單元。
2.多線程編程
- 多任務可以由多進程完成,也可以由一個進程中的多線程完成(一個進程至少有一個進程)
3.Python中模塊
- _thread和threading。_thread是低級模塊,threading是高級模塊,對_thread進行了封裝。絕大多數情況下我們使用的是threading高級模塊
3.啓動一個線程就是把一個函數傳入並創建Thread實例,然後調用start()開始執行
import threading import time def loop(x): print("%s start" % threading.current_thread().name) for i in range(x): time.sleep(1) print("%s : %d" % (threading.current_thread().name, i)) # print("%s stop" % threading.current_thread().name) print("%s stop" % threading.current_thread().name) print("%s start" % threading.current_thread().name) t1 = threading.Thread(target=loop, args=(6,)) t1.start() print("%s stop" % threading.current_thread().name)
結果:
MainThread start
Thread-1 startMainThread stop
Thread-1 : 0
Thread-1 : 1
Thread-1 : 2
Thread-1 : 3
Thread-1 : 4
Thread-1 : 5
Thread-1 stop
結果分析:主線程先運行完畢,再運行子線程
4.如果希望主線程等待子線程執行完畢,使用join()方法
import threading import time def loop(x): print("%s start" % threading.current_thread().name) for i in range(x): time.sleep(1) print("%s : %d" % (threading.current_thread().name, i)) # print("%s stop" % threading.current_thread().name) print("%s stop" % threading.current_thread().name) print("%s start" % threading.current_thread().name) t1 = threading.Thread(target=loop, args=(6,)) t1.start() t1.join() print("%s stop" % threading.current_thread().name)
結果:
MainThread start
Thread-1 start
Thread-1 : 0
Thread-1 : 1
Thread-1 : 2
Thread-1 : 3
Thread-1 : 4
Thread-1 : 5
Thread-1 stop
MainThread stop
結果分析:MainThread 一直停在join位置,直到子線程運行完畢
6.主線程退出的時候,不管子線程運行到哪裏,強制退出子線程,使用setDaemon(True), 這個方法在線程啓動之前調用
import threading import time def loop(x): print("%s start" % threading.current_thread().name) for i in range(x): time.sleep(1) print("%s : %d" % (threading.current_thread().name, i)) # print("%s stop" % threading.current_thread().name) print("%s stop" % threading.current_thread().name) print("%s start" % threading.current_thread().name) t1 = threading.Thread(target=loop, args=(6,)) t1.setDaemon(True) t1.start() # t1.join() print("%s stop" % threading.current_thread().name)
結果:
MainThread start
Thread-1 start
MainThread stop
7.線程鎖(lock)
多線程和多進程最大的不同在於,多進程同一變量各自有一份拷貝存在每個進程中,互不影響,而多線程所有變量由線程共享,所以任何一個變量都可以由任何一個線程修改,因此線程間共享數據最大的危險在與多個線程同時改變一個變量,把內容給改亂了。解決方法:使用threading.Lock()方法,使用線程鎖鎖定線程需要使用的對象,確保一次只有一個線程能夠使用該對象。
實例:多線程操作同一對象出錯
import threading deposit = 0 # 存款 def change(n): global deposit deposit = deposit + n deposit = deposit - n def loop(n): for i in range(10000000): change(n) t1 = threading.Thread(target=loop, args=(5,)) t2 = threading.Thread(target=loop, args=(8,)) t1.start() t2.start() t1.join() t2.join() print(deposit)
結果:
45
結果分析:t1,t2交替運行,當兩個線程同時訪問一個對象時候,容易出錯
使用線程鎖結果該問題:
import threading deposit = 0 # 存款 def change(n): global deposit deposit = deposit + n deposit = deposit - n def loop(n): for i in range(10000000): lock.acquire() # 取得一個鎖 try: change(n) finally: lock.release() # 釋放一個鎖 lock = threading.Lock() # 創建一個鎖對象 t1 = threading.Thread(target=loop, args=(5,)) t2 = threading.Thread(target=loop, args=(8,)) t1.start() t2.start() t1.join() t2.join() print(deposit)
結果:
0
8.線程鎖討論
線程鎖確保某段關鍵代碼只能由一個線程從頭到尾完整執行,但是線程鎖阻止了多線程併發執行,包含鎖的某段代碼實際上只能以單線程模式執行,效率大大降低。其次由於可以存在多個鎖,不同線程支持不同的鎖,試圖獲得對方的鎖時,可能會造成死鎖。
9.全局解釋器鎖(GIL)
任何python線程執行前都必須獲得GIL鎖,執行代碼直到線程睡眠或者python將它掛起,線程釋放GIL。一個處理核心只能在同一時間運行一個線程。Python中可以使用多線程,但不能有效的利用多個處理核心。可以使用多進程來利用多核。
cpu密集型(各種循環,計數等):遇到io操作或者ticks超過100(每執行100條字節碼),釋放GIL。觸發GIL競爭,線程間的來回切換需要消耗資源,故而多線程對CPU密集型代碼並不友好。
io密集型(文件處理/網絡爬蟲):動線程能提升效率(單線程io會進行io等待,造成不必要的時間浪費,開啓多線程可以讓進程A等待時自動切換到線程B,可以不浪費cpu資源,提升效率)
10.爲什麼多進程不會有上述情況。
每一個進程都有各自獨立的GIL,互不影響。