本章內容python
線程是最小的調度單位
進程是最小的管理單元編程
#併發 import time ,threading def test1(n): for i in range(n): time.sleep(1) print('task1',i) def test2(n): for i in range(n): time.sleep(1) print('task2',i) thread1 = threading.Thread(target=test1,args=(10,)) thread2 = threading.Thread(target=test2,args=(10,)) thread1.start() thread2.start() ....................................... #運行共用10s
#無序 import time,threading def test1(n): time.sleep(1) print('task',n) for i in range(10): thread1 = threading.Thread(target=test1,args=(i,)) thread1.start() ...............................運行結果 task 1 task 2 task 3 task 7 task 5 task 4 task 6 task 0 task 9 task 8
#共享全局變量 import threading num=0 def test1(): global num for i in range(10): num+=1 def test2(): global num print(num) thread1 = threading.Thread(target=test1) thread2 = threading.Thread(target=test2) thread1.start() thread2.start() ..............................................運行結果 10
#GIL全局解釋器鎖實例 import threading global_num = 0 def test1(): global global_num for i in range(1000000): global_num += 1 def test2(): global global_num for i in range(1000000): global_num += 1 t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start() print(global_num) ......................................運行結果 109704 #運行結果本應該爲2000000,爲何運行結果遠小於應得結果?
第一個緣由:運行代碼後加上咱們啓動的兩個線程,cpu共起了3個線程;其中一個用於執行代碼(取名a),兩個用於執行函數test1和test2(t1 t2);a線程運行到print(global_num)時,test1和test2循環尚未執行完畢!線程a就已經輸出了;因此獲得的值不是2000000多線程
解決方法:讓a線程等test1和test2循環完成以後再執行print();併發
#緣由1解決後 import threading global_num = 0 def test1(): global global_num for i in range(1000000): global_num += 1 def test2(): global global_num for i in range(1000000): global_num += 1 t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start() t1.join() t2.join() print(global_num) ...........................................運行結果 1240490 #爲何仍是不等於2000000呢?
第二個緣由:GIL全局解釋器鎖app
GIL全局解釋器鎖:Global Interpreter Lock,意思就是全局解釋器鎖,這個GIL並非python的特性,他是隻在Cpython解釋器裏引入的一個概念;隨着電腦多核cpu的出現核cpu頻率的提高,爲了充分利用多核處理器,進行多線程的編程方式更爲普及,隨之而來的困難是線程之間數據的一致性和狀態同步,而python也利用了多核,因此也逃不開這個困難,爲了解決這個數據不能同步的問題,設計了gil全局解釋器鎖。異步
gil全局解釋器鎖的做用:多線程共享全局變量的時候,gil全局解釋器鎖能夠保證同一時間內只有一個線程能夠拿到這個全局變量;async
看圖講解:函數
- t1拿到全局變量count=0
- t1申請gil鎖(gil全局解釋器鎖能夠保證只有一個線程能夠拿到這個全局變量)
- t1調用系統線程
- t1到cpu上執行+1操做
- t1執行操做時可能偷懶了,沒加完,但時間到了t1只能把gil鎖給了t2
- t2在此以前已經拿到count全局變量count=0(注意此時覺得t1沒執行完+1操做,count=0)
- t2拿到gil鎖
- t2調用系統線程
- t2到cpu上執行+1操做
- t2勤勤懇懇的+1,在時間內完成了加一操做
- t2 完成給count賦值(此時count=1)
- t1又拿到gil鎖,繼續未完成的操做,
- t1完成操做並賦值給count=1,(此時count仍是=1)
#解決方法:調用threading模塊的lock鎖 import threading global_num = 0 lock=threading.Lock() #實例化一個lock鎖 def test1(): global global_num lock.acquire() #鎖上 for i in range(1000000): global_num += 1 lock.release() #解鎖 def test2(): global global_num lock.acquire() #鎖上 for i in range(1000000): global_num += 1 lock.release() #解鎖 t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start() t1.join() t2.join() print(global_num) .....................................運行結果 2000000
#多線程實現多任務 import multiprocessing,time num = 10 def test1(): for i in range(num): time.sleep(1) print('task1',i) def test2(): for i in range(num): time.sleep(1) print('task2',i) if __name__ == '__main__': p1 = multiprocessing.Process(target=test1) p2 = multiprocessing.Process(target=test2) p1.start() p2.start() .............................運行結果 #共運行10s
#相互獨立資源不共享 import multiprocessing num = 0 def test1 (): global num for i in range(10): num+=1 print(num) def test2(): global num for i in range(10): num+=1 print(num) if __name__ == '__main__': p1 = multiprocessing.Process(target=test1) p2 = multiprocessing.Process(target=test2) p1.start() p2.start() ...............................運行結果 10 10
#進程池 import time ,multiprocessing from multiprocessing import Pool def test1(n): for i in range(n): time.sleep print('task1',i) def test2(n): for i in range(n): time.sleep(1) print('task2',i) if __name__ == '__main__': pool = Pool(1) #容許最大進程數 pool.apply_async(test1,(10,)) #參數必須元組 pool.apply_async(test2,(10,)) pool.close() pool.join()
協程:也叫微線程,協程是在一個線程裏面的
現有進程---> 線程 ---> 協程ui
- 進程是資源分配的單位
- 線程是操做系統調度的單位
- 協程的調度由所在程序自身控制
- 進程切換須要的資源最大,效率低
- 線程切換須要的資源通常,效率通常
- 協程切換任務資源很小,效率高
- 多進程、多線程根據cpu核數不同多是並行的,可是協成在一個線程中
異步IO:遇到io請求就切換
第一步:pip 安裝gevent模塊spa
pip install gevent
協程實例:
#異步io import gevent def test1(n): for i in range(n): gevent.sleep(1) print('task1',i) def test2(n): for i in range(n): gevent.sleep(1) print('task2',i) g1 = gevent.spawn(test1,10) #協程任務g1 g2 = gevent.spawn(test2,10) #任務g2 g1.join() g2.join() ..........................運行結果 #運行共用時10s
協程徹底靠異步IO完成兩個任務:遇到io請求就切換(sleep算IO請求)