1.線程列隊 queue隊列 :使用import queue,用法與進程Queue一樣 class queue.Queue(maxsize=0) 1 # 先進先出: 2 q = queue.Queue(3) # 也可以不加數字表示不限 3 q.put('約嗎') 4 q.put('你個糟老頭') 5 ...
1.線程列隊
queue隊列 :使用import queue,用法與進程Queue一樣
class queue.Queue(maxsize=0)
1 # 先進先出: 2 q = queue.Queue(3) # 也可以不加數字表示不限 3 q.put('約嗎') 4 q.put('你個糟老頭') 5 q.put('約個鬼!') 6 # q.put_nowait() # 沒有數據會報錯 可以try解決 7 print(q.get()) 8 print(q.get()) 9 print(q.get()) 10 q.get_nowait()先進先出
class queue.LifoQueue(maxsize=0)
1 import queue 2 3 # 後進先出: 4 q = queue.LifoQueue(4) 5 q.put('first') 6 q.put('second') 7 q.put('third') 8 q.put_nowait(1) 9 print(q.get()) 10 print(q.get()) 11 print(q.get()) 12 print(q.get_nowait())後進先出
class queue.PriorityQueue(maxsize=0)
1 # 設置優先 2 q = queue.PriorityQueue(6) 3 4 # put進入元組,元組第一個元素是優先順序(通常是數字,也可以是非數字之間的比較),數字越小優先順序越高 5 q.put((3,'a')) 6 q.put((2,'b')) 7 q.put((-3,'c')) 8 9 # 優先順序相同的兩個數據,比較第二個元素ASCII碼大小,若第二元素為字元串且第一個字元相同則比較第二個 10 q.put((20,'ww')) 11 q.put((20,'ws')) 12 # q.put(20,{'ws',22}) # 不能是字典 13 # q.put((20,('w',1))) # 後面的值必須是相同數據類型才能比較,可以是元祖,ascii碼順序排序 14 15 print(q.get()) 16 print(q.get()) 17 print(q.get()) 18 print(q.get()) 19 print(q.get()) 20 # print(q.get())View Code
這三種隊列都是線程安全的,不會出現多個線程搶占同一個資源或數據的情況。
2.線程池
線程池:早期的時候沒有線程池,現在python提供了一個新的內置模塊 concurrent.futures,模塊裡面提供了新的線程池和進程池,之前的進程池是在multiprocessing裡面,現在這個在這個新的模塊裡面,他倆用法上是一樣的。為了統一使用方式將進程池和線程池放到一起,使用threadPollExecutor和ProcessPollExecutor的方式一樣,且只要通過concurrent.futures導入就可用他們兩個
基本方法:
1 #2 基本方法 2 #submit(fn, *args, **kwargs) 3 非同步提交任務 4 5 #map(func, *iterables, timeout=None, chunksize=1) 6 取代for迴圈submit的操作 7 8 #shutdown(wait=True) 9 相當於進程池的pool.close()+pool.join()操作 10 wait=True,等待池內所有任務執行完畢回收完資源後才繼續 11 wait=False,立即返回,並不會等待池內的任務執行完畢 12 但不管wait參數為何值,整個程式都會等到所有任務執行完畢 13 submit和map必須在shutdown之前 14 15 #result(timeout=None) 16 取得結果 17 18 #add_done_callback(fn) 19 回調函數
1 import time 2 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor 3 4 5 def func(n): 6 time.sleep(0.5) 7 return n*n 8 9 10 if __name__ == '__main__': 11 t_pool = ThreadPoolExecutor(max_workers=4) 12 # t_pool = ProcessPoolExecutor(max_workers=4) # 用concurrent.futures可直接轉換成進程 13 t_list = [] 14 for i in range(10): 15 res = t_pool.submit(func,i) # submit非同步提交任務 16 # print(res.result()) # 等待res的執行結果,拿到了就運行,拿不到就阻塞,放這會變成串列 17 t_list.append(res) 18 19 t_pool.shutdown() # 相當於進程池的pool.close() pool.join() 20 21 print('主線程結束') 22 23 for res1 in t_list: 24 print(res1.result())線程池代碼示例
1 import time 2 from concurrent.futures import ThreadPoolExecutor 3 4 5 def func(n): 6 time.sleep(1) 7 return n*n 8 9 10 if __name__ == '__main__': 11 12 t_pool = ThreadPoolExecutor(6) 13 res = t_pool.map(func,range(10)) # map 取代for迴圈submit的操作 14 t_pool.shutdown() 15 16 print('主線程執行結束') 17 for i in res: 18 print(i) 19 20 # map包含了for迴圈和submit操作map方法
1 import time 2 from concurrent.futures import ThreadPoolExecutor 3 4 5 def func1(n): 6 time.sleep(1) 7 return n*n 8 9 10 def callback(s): 11 # print(s) 12 ss = s.result()+1 13 print(ss) 14 15 16 if __name__ == '__main__': 17 t_pool = ThreadPoolExecutor(4) 18 for i in range(10): 19 t_pool.submit(func1,i).add_done_callback(callback) 20 21 # 註意map函數沒有callback,不能如下使用 22 # res = t_pool.map(func1,range(10)) 23 # for i in res: 24 # i.add_done_callback(callback)回調函數
需註意:註意map函數沒有callback
3.GIL鎖
背景:
一些語言(java、c++、c)是支持同一個進程中的多個線程是可以應用多核CPU的,也就是我們會聽到的現在4核8核這種多核CPU技術的牛逼之處。那麼我們之前說過應用多進程的時候如果有共用數據是不是會出現數據不安全的問題啊,就是多個進程同時一個文件中去搶這個數據,大家都把這個數據改了,但是還沒來得及去更新到原來的文件中,就被其他進程也計算了,導致數據不安全的問題啊,所以我們是不是通過加鎖可以解決啊,多線程大家想一下是不是一樣的,併發執行就是有這個問題。但是python最早期的時候對於多線程也加鎖,但是python比較極端的(在當時電腦cpu確實只有1核)加了一個GIL全局解釋鎖,是解釋器級別的,鎖的是整個線程,而不是線程裡面的某些數據操作,每次只能有一個線程使用cpu,也就說多線程用不了多核,但是他不是python語言的問題,是CPython解釋器的特性,如果用Jpython解釋器是沒有這個問題的,Cpython是預設的,因為速度快,Jpython是java開發的,在Cpython裡面就是沒辦法用多核,這是python的弊病,歷史問題,雖然眾多python團隊的大神在致力於改變這個情況,但是暫沒有解決。(這和解釋型語言(python,php)和編譯型語言有關係嗎???待定!,編譯型語言一般在編譯的過程中就幫你分配好了,解釋型要邊解釋邊執行,所以為了防止出現數據不安全的情況加上了這個鎖,這是所有解釋型語言的弊端??)
如圖:
但是有了這個鎖我們就不能併發了嗎?當我們的程式是偏計算的,也就是cpu占用率很高的程式(cpu一直在計算),就不行了,但是如果你的程式是I/O型的(一般你的程式都是這個)(input、訪問網址網路延遲、打開/關閉文件讀寫),在什麼情況下用的到高併發呢(金融計算會用到,人工智慧(阿爾法狗),但是一般的業務場景用不到,爬網頁,多用戶網站、聊天軟體、處理文件),I/O型的操作很少占用CPU,那麼多線程還是可以併發的,因為cpu只是快速的調度線程,而線程裡面並沒有什麼計算,就像一堆的網路請求,我cpu非常快速的一個一個的將你的多線程調度出去,你的線程就去執行I/O操作了
GIL鎖與Lock:
GIL保護的是解釋器級的數據,保護用戶自己的數據則需要自己加鎖處理,如下圖