線程python
首先弄清進程和線程之間的區別,這一點是很是重要的。線程與進程的不一樣之處在於,它們共享狀態、內存和資源。對於線程來講,這個簡單的區別既是它的優點,又是它的缺點。一方面,線程是輕量級的,而且相互之間易於通訊,但另外一方面,它們也帶來了包括死鎖、爭用條件和高複雜性在內的各類問題。幸運的是,因爲 GIL 和隊列模塊,與採用其餘的語言相比,採用 Python 語言在線程實現的複雜性上要低得多。不管是建立進程或者線程都是爲了實現併發操做git
Python進程、線程之間的原理圖程序員
計算機有進程和線程的目的:提升執行效率
計算機默認有主進程和主線程github
進程:
優勢:同時利用多個CPU,可以同時進行多個操做
缺點:耗費資源(從新開闢內存空間)
進程不是越多越好,理論上CPU個數(核數)=進程個數
計算密集型適用於進程,由於計算之類的須要CPU運算(佔用CPU)
線程:
優勢:共享內存,IO操做時,創造併發操做
缺點:槍戰資源
線程不是越多越好,具體案例具體分析,請求上下文切換耗時
IO密集型適用於線程,IO操做打開文件網絡通信類,不須要佔用CPU,只是由CPU調度一下(不佔用CPU)算法
自定義進程和線程:注意python解釋器自帶了主進程和主線程,好比在代碼文件裏沒有定義線程和進程,程序也能運行就是靠的解釋器自帶主進程的主線程執行的api
自定義進程:
由主進程建立,子進程
自定義線程:
由主線程建立,子線程數組
GIL全局解釋器鎖:瀏覽器
GIL全局解釋器鎖在進程入口,控制着進程數量與CPU的相應安全
線程是應用程序中工做的最小單元網絡
threading 模塊創建在 _thread 模塊之上。thread 模塊以低級、原始的方式來處理和控制線程,而 threading 模塊經過對 thread 進行二次封裝,提供了更方便的 api 來處理線程。
Thread()建立線程對象【有參】
使用方法:賦值變量 = 模塊名稱.Thread(target=事件函數,args=元祖類型事件函數的實際參數) 如函數多個參數,元祖裏就是多個元素
格式:t = threading.Thread(target=show, args=(i,))
currentThread()獲取當前線程【無參】
使用方法:自定義變量 = threading模塊名稱.currentThread()
格式:current_thread = threading.currentThread()
start()激活線程【無參】
使用方法:thread對象變量.start()
格式:t.start()
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.start() #激活子線程 print("默認主線程等待子線程完成任務後,主線程中止") # 輸出 # 默認主線程等待子線程完成任務後,主線程中止 # 線程0 # 線程5 # 線程8 # 線程3 # 線程6 # 線程4 # 線程1 # 線程7 # 線程2 # 線程9
上述代碼建立了10個「前臺」線程,而後控制器就交給了CPU,CPU根據指定算法進行調度,分片執行指令。
自定義線程類
import threading import time class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self):#定義每一個線程要運行的函數 print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()
getName()獲取線程的名稱【無參】
使用方法:thread對象變量.getName()
格式:t.getName()
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.start() #激活子線程 print(t.getName()) #獲取線程的名稱 print("默認主線程等待子線程完成任務後,主線程中止") # 輸出 # Thread-1 # Thread-2 # Thread-3 # Thread-4 # Thread-5 # Thread-6 # Thread-7 # Thread-8 # Thread-9 # Thread-10 # 默認主線程等待子線程完成任務後,主線程中止 # 線程2 # 線程1 # 線程0 # 線程9 # 線程8 # 線程6 # 線程3 # 線程5 # 線程7 # 線程4
setName()設置線程的名稱【有參】
使用方法:thread對象變量.setName("要設置的線程名稱")
格式:t.setName("jhf")
name獲取或設置線程的名稱【無參】
使用方法:thread對象變量.name
格式:t.name
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.setName("jhf") #設置線程的名稱 print(t.name) #獲取或設置線程的名稱 t.start() #激活子線程 print("默認主線程等待子線程完成任務後,主線程中止") # 輸出 # jhf # jhf # jhf # jhf # jhf # jhf # jhf # jhf # jhf # jhf # 默認主線程等待子線程完成任務後,主線程中止 # 線程1 # 線程0 # 線程2 # 線程7 # 線程5 # 線程3 # 線程4 # 線程9 # 線程8 # 線程6
is_alive()判斷線程是否爲激活狀態返回布爾值【無參】
使用方法:thread對象變量.is_alive()
格式:t.is_alive()
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.start() #激活子線程 a = t.is_alive() #判斷線程是否爲激活狀態返回布爾值 print(a) #打印出返回值 print("默認主線程等待子線程完成任務後,主線程中止") # 輸出 # True # 默認主線程等待子線程完成任務後,主線程中止 # 線程3 # 線程4 # 線程2 # 線程5 # 線程1 # 線程0 # 線程6 # 線程7 # 線程8 # 線程9
isAlive()判斷線程是否爲激活狀態返回布爾值【無參】
使用方法:thread對象變量.isAlive()
格式:t.isAlive()
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.start() #激活子線程 a = t.isAlive() #判斷線程是否爲激活狀態返回布爾值 print(a) #打印出返回值 print("默認主線程等待子線程完成任務後,主線程中止") # 輸出 # True # 默認主線程等待子線程完成任務後,主線程中止 # 線程3 # 線程4 # 線程2 # 線程5 # 線程1 # 線程0 # 線程6 # 線程7 # 線程8 # 線程9
setDaemon() 設置爲後臺線程或前臺線程,也就是定義主線程是否等待子線程執行完畢後,主線程才中止【有參】
(默認:False);經過一個布爾值設置線程是否爲守護線程,必須在執行start()方法以後纔可使用。若是是後臺線程,主線程執行過程當中,後臺線程也在進行,主線程執行完畢後,後臺線程不論成功與否,均中止;若是是前臺線程,主線程執行過程當中,前臺線程也在進行,主線程執行完畢後,等待前臺線程也執行完成後,程序中止
使用方法:thread對象變量.setDaemon(布爾值)
格式:t.setDaemon(True)
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.setDaemon(True) #設置爲後臺線程或前臺線程,也就是定義主線程是否等待子線程執行完畢後,主線程才中止 t.start() #激活子線程 # 輸出 # 主線沒等子線程執行完,主線程就中止了,因此沒輸出信息
isDaemon()判斷是否爲守護線程,也就是主線程是否等待子線程執行完成後,才中止主線程,返回布爾值【無參】
使用方法:thread對象變量.isDaemon()
格式:t.isDaemon()
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.start() #激活子線程 a = t.isDaemon() #判斷是否爲守護線程,也就是主線程是否等待子線程執行完成後,才中止主線程返回布爾值 print(a) #打印布爾值 # 輸出 # False # 線程1 # 線程3 # 線程0 # 線程5 # 線程2 # 線程4 # 線程9 # 線程6 # 線程8 # 線程7
ident獲取線程的標識符。線程標識符是一個非零整數,只有在調用了start()方法以後該屬性纔有效,不然它只返回None。【無參】
使用方法:thread對象變量.ident
格式:t.ident
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(3) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.start() #激活子線程 a = t.ident #獲取線程的標識符。線程標識符是一個非零整數,只有在調用了start()方法以後該屬性纔有效,不然它只返回None。 print(a) #打印線程的標識符 # 輸出 # 10040 # 13172 # 12096 # 4456 # 10200 # 844 # 2200 # 2440 # 2968 # 12756 # 線程3 # 線程2 # 線程1 # 線程0 # 線程7 # 線程9 # 線程8 # 線程4 # 線程5 # 線程6
join()逐個執行每一個線程,等待一個線程執行完畢後繼續往下執行,該方法使得多線程變得無心義【有參可選】
有參可選,參數爲等待時間,秒爲單位,如t.join(1) 就是一個線程不在是等待它執行完,而是隻等待它1秒後繼續下一個線程
使用方法:thread對象變量.join()
格式:t.join()
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 def show(arg): #定義函數 time.sleep(1) #睡眠3秒 print('線程'+str(arg)) #打印線程加循環次數 for i in range(10): #定義一個10次循環 t = threading.Thread(target=show, args=(i,)) #用threading模塊的Thread類來建立子線程對象 t.start() #激活子線程 t.join() #逐個執行每一個線程,執行完畢後繼續往下執行,該方法使得多線程變得無心義 # 輸出 # 線程0 # 線程1 # 線程2 # 線程3 # 線程4 # 線程5 # 線程6 # 線程7 # 線程8 # 線程9
run()線程被cpu調度後自動執行線程對象的run方法
使用方法:thread對象變量.run()
格式:t.run()
咱們使用線程對數據進行操做的時候,若是多個線程同時修改某個數據,可能會出現不可預料的結果,爲了保證數據的準確性,引入了鎖的概念。
沒有線程鎖的狀況列如:一個全局變量值爲50,建立10條線程讓每條線程累計減一,輸出的結果是10個40,緣由是10條線程同時減一就減去了10,因此打印出來就是10個40了
未使用鎖
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 globals_num = 50 #設置一個變量 def Func(a): #定義一個函數 global globals_num #將變量轉換成全局變量,函數裏能夠調用 globals_num -= 1 #全局變量減1 time.sleep(1) #睡眠1秒 print(globals_num,a) #打印全局變量減小後的結果,和函數傳進來的值 for i in range(10): #建立一個10次循環 t = threading.Thread(target=Func,args=(i,)) #建立線程對象 t.start() #激活線程 # 輸出 沒有線程鎖,線程之間搶佔了數據資源 # 40 5 # 40 3 # 40 6 # 40 4 # 40 0 # 40 2 # 40 1 # 40 9 # 40 8 # 40 7
根據上列狀況能夠看出,沒有線程鎖,線程之間搶佔了數據資源
線程鎖就是將線程鎖定,一個線程執行完畢後釋放鎖後在執行第二個線程
RLock()定義線程鎖對象
使用方法:定義對象變量 = threading模塊名稱.RLock()
格式:lock = threading.RLock()
acquire()得到鎖,將線程鎖定,一個線程執行完畢釋放鎖後在執行第二個線程
使用方法:線程鎖對象變量.acquire()
格式:lock.acquire()
release()釋放線程鎖
使用方法:線程鎖對象變量.release()
格式:lock.release()
使用鎖
#!/usr/bin/env python # -*- coding:utf8 -*- import threading #導入線程模塊 import time #導入時間模塊 globals_num = 50 #設置一個變量 lock = threading.RLock() def Func(a): #定義一個函數 lock.acquire() # 得到鎖,將線程鎖定,一個線程執行完畢後在執行第二個線程 global globals_num #將變量轉換成全局變量,函數裏能夠調用 globals_num -= 1 #全局變量減1 time.sleep(1) #睡眠1秒 print(globals_num,a) #打印全局變量減小後的結果,和函數傳進來的值 lock.release() # 釋放鎖 for i in range(10): #建立一個10次循環 t = threading.Thread(target=Func,args=(i,)) #建立線程對象 t.start() #激活線程 # 輸出 將線程鎖定,一個線程執行完畢後在執行第二個線程 # 49 0 # 48 1 # 47 2 # 46 3 # 45 4 # 44 5 # 43 6 # 42 7 # 41 8 # 40 9
RLock容許在同一線程中被屢次acquire。而Lock卻不容許這種狀況。 若是使用RLock,那麼acquire和release必須成對出現,即調用了n次acquire,必須調用n次的release才能真正釋放所佔用的瑣。
import threading lock = threading.Lock() #Lock對象 lock.acquire() lock.acquire() #產生了死瑣。 lock.release() lock.release()
import threading rLock = threading.RLock() #RLock對象 rLock.acquire() rLock.acquire() #在同一線程內,程序不會堵塞。 rLock.release() rLock.release()
Event()建立標識事件對象,全局定義了一個「Flag」,若是「Flag」值爲 False,那麼當程序執行 event.wait 方法時就會阻塞,若是「Flag」值爲True,那麼event.wait 方法時便再也不阻塞
python線程的事件用於主線程控制其餘線程的執行,事件主要提供了三個方法 set、wait、clear。
事件處理的機制:全局定義了一個「Flag」,若是「Flag」值爲 False,那麼當程序執行 event.wait 方法時就會阻塞,若是「Flag」值爲True,那麼event.wait 方法時便再也不阻塞。
當線程執行的時候,若是flag爲False,則線程會阻塞,當flag爲True的時候,線程不會阻塞。它提供了本地和遠程的併發性。
wait([timeout]) : 堵塞線程,直到Event對象內部標識位被設爲True或超時(若是提供了參數timeout)。
set() :將標識位設爲Ture
clear() : 將標識位設爲False。
isSet() :判斷標識位是否爲Ture。
#!/usr/bin/env python # -*- coding:utf8 -*- import threading def do(event): print('start') event.wait() #堵塞線程,直到Event對象內部標識位被設爲True或超時(若是提供了參數timeout) print('execute') event_obj = threading.Event() #建立標識事件對象 for i in range(10): t = threading.Thread(target=do, args=(event_obj,)) #建立線程對象 t.start() #激活線程 event_obj.clear() #將標識設置爲False inp = input('input:') if inp == 'true': event_obj.set() #將標識設置爲True # 輸出 # start # start # start # start # start # start # start # start # start # input:true # execute # execute # execute # execute # execute # execute # execute # execute # execute # execute
threading.BoundedSemaphore信號對象
是同時容許必定數量的線程更改數據 ,好比廁全部3個坑,那最多隻容許3我的上廁所,後面的人只能等裏面有人出來了才能再進去。
BoundedSemaphore()建立信號對象【有參】
使用方法:定義變量.threading.BoundedSemaphore(最大容許線程數)
格式:semaphore = threading.BoundedSemaphore(5)
BoundedSemaphore信號對象的方法有
acquire()獲取信號
release()釋放信號
#!/usr/bin/env python # -*- coding:utf8 -*- import threading,time #導入線程模塊,和時間模塊 semaphore = threading.BoundedSemaphore(5) #最多容許5個線程同時運行 def run(n): #建立函數 semaphore.acquire() #獲取信號 time.sleep(1) print("run the thread: %s" %n) #semaphore.release() #釋放信號 for i in range(20): t = threading.Thread(target=run,args=(i,)) t.start() # 輸出 # run the thread: 3 # run the thread: 2 # run the thread: 0 # run the thread: 1 # run the thread: 4
threading.Condition條件對象
使得線程等待,只有知足某條件時,才釋放n個線程
Condition()建立條件對象【無參】
使用方法:定義變量.threading.Condition()
格式:con = threading.Condition()
Condition條件對象的方法有
acquire()
wait()
release()
notify()
#!/usr/bin/env python # -*- coding:utf8 -*- import threading con = threading.Condition() def run(n): con.acquire() con.wait() print("run the thread: %s" %n) con.release() for i in range(10): t = threading.Thread(target=run, args=(i,)) t.start() while True: inp = input('>>>') if inp == 'q': break con.acquire() con.notify(int(inp)) con.release()
Queue 就是對隊列,它是線程安全的
舉例來講,咱們去肯德基吃飯。廚房是給咱們作飯的地方,前臺負責把廚房作好的飯賣給顧客,顧客則去前臺排隊領取作好的飯。這裏的前臺就至關於咱們的隊列。
這個模型也叫生產者-消費者模型。
Queue()建立隊列對象【有參】
使用方法:定義變量 = queue.Queue(對列長度數) 0表示長度無限制
格式:message = queue.Queue(10)
Queue對象方法有:
join()等到隊列爲空的時候,在執行別的操做【無參】
qsize()返回隊列的大小(不可靠),由於獲取後有可能有新數據加入【無參】
empty()清空隊列裏的全部數據
full()檢查隊列是否爲滿,當隊列滿的時候,返回True,不然返回False(不可靠),由於獲取後有可能有新數據加入【無參】
put(放入對列的數據必選, block=True, timeout=None) 向隊列裏放入數據(生產)【有參】
將數據放入對列尾部(生產),數據必須存在,能夠參數block默認爲True,表示當隊列滿時,會等待隊列給出可用位置,爲False時爲非阻塞,此時若是隊列已滿,會引起queue.Full 異常。
可選參數timeout,表示 會阻塞設置的時間,事後,若是隊列沒法給出放入item的位置,則引起 queue.Full 異常
get(block=True, timeout=None)移除並返回隊列頭部的一個值(消費)【有參】
可選參數block默認爲True,表示獲取值的時候,若是隊列爲空,則阻塞,爲False時,不阻塞,若此時隊列爲空,則引起 queue.Empty異常。
可選參數timeout,表示會阻塞設置的時候,事後,若是隊列爲空,則引起Empty異常。
put_nowait(放入對列的數據必選)向隊列裏放入數據(生產)【有參】,若是隊列滿時不阻塞,不等待隊列給出可用位置,引起 queue.Full 異常
get_nowait()移除並返回隊列頭部的一個值(消費)【無參】,若是隊列空時不阻塞,引起 queue.Full 異常
對列模型-生產者-消費者
#!/usr/bin/env python # -*- coding:utf8 -*- import queue #導入列隊模塊 import threading #導入線程模塊 message = queue.Queue(10) #定義列隊對象,設置列隊長度 def producer(i): #定義生產者函數 while True: message.put("生產") #向隊列裏放數據 def consumer(i): #定義消費者函數 while True: msg = message.get() #從隊列裏取數據 print(msg) #打印出從隊列裏取出 for i in range(12): #建立12條線程生產,也就是有12條線程向隊列裏放數據 t = threading.Thread(target=producer, args=(i,)) #建立線程對象 t.start() #激活線程 for i in range(10): #建立10條線程消費,也就是有10條線程從列隊裏取數據 t = threading.Thread(target=consumer, args=(i,)) #建立線程對象 t.start() #激活線程 # 輸出 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產 # 生產
對列模型-生產者-消費者原理圖
multiprocessing進程模塊
multiprocessing是python的多進程管理包,和threading.Thread相似。直接從側面用subprocesses替換線程使用GIL的方式,因爲這一點,multiprocessing模塊可讓程序員在給定的機器上充分的利用CPU。
在multiprocessing中,經過建立Process對象生成進程,而後調用它的start()方法,
Process()建立進程對象【有參】
注意:wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題
使用方法:定義變量 = multiprocessing.Process(target=要建立進程的函數, args=元祖類型要建立進程函數的參數、多個參數逗號隔開)
格式:t = multiprocessing.Process(target=f1, args=(133,))
start()激活進程【無參】
使用方法:Process對象變量.start()
格式:t.start()
建立10條進程
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 def f1(r): #建立函數 print(r) #打印傳值 if __name__ == "__main__": #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 for i in range(10): #循環10次,建立10條進程 t = multiprocessing.Process(target=f1, args=(133,)) #建立進程對象 t.start() #激活進程 # 輸出 # 133 # 133 # 133 # 133 # 133 # 133 # 133 # 133 # 133 # 133
daemon主進程是否等待子進程執行完畢後,在中止主進程,daemon=True(主進程不等待子進程)、daemon=False(主進程等待子進程)
使用方法:Process對象變量.daemon=True或者False
格式:t.daemon = False
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 def f1(r): #建立函數 print(r) #打印傳值 if __name__ == "__main__": #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 for i in range(10): #循環10次,建立10條子進程 t = multiprocessing.Process(target=f1, args=(133,)) #建立進程對象 t.daemon = True #主進程是否等待子進程執行完畢後,在中止主進程 t.start() #激活進程 # 輸出 #daemon = False 主進程沒等子進程執行完,主進程就中止了,因此沒有打印出信息
join()逐個執行每一個進程,等待一個進程執行完畢後繼續往下執行,該方法使得進程程變得無心義【有參可選】
有參可選,參數爲等待時間,秒爲單位,如t.join(1) 就是一個進程不在是等待它執行完,而是隻等待它1秒後繼續下一個進程
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 import time def f1(r): #建立函數 time.sleep(1) print(r) #打印傳值 if __name__ == "__main__": #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 for i in range(10): #循環10次,建立10條子進程 t = multiprocessing.Process(target=f1, args=(133,)) #建立進程對象 t.start() #激活進程 t.join() #逐個執行每一個進程,等待一個進程執行完畢後繼續往下執行 # 輸出 # 133 # 133 # 133 # 133
進程各自持有一份數據,默認沒法共享數據
因此至關於每個進程有一份本身的數據,每一個進程操做數據時,操做的屬於本身的一份數據
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 li = [] #建立空列表 def f1(i): #建立函數 li.append(i) #追加列表 print("列表",li) #打印追加後的列表 if __name__ == "__main__": #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 for i in range(10): #循環10次,建立10條子進程,進程各自持有一份數據,默認沒法共享數據,因此至關於每個進程有一個f1函數,每一個進程在追加列表時追加的屬於本身的一份f1函數 t = multiprocessing.Process(target=f1, args=(i,)) #建立進程對象 t.start() #激活進程 # 輸出 # 列表 [0] # 列表 [2] # 列表 [5] # 列表 [3] # 列表 [1] # 列表 [6] # 列表 [8] # 列表 [7] # 列表 [4] # 列表 [9]
進程原理圖
注意:因爲進程之間的數據須要各自持有一份,因此建立進程須要的很是大的開銷。
進程數據共享
注意:進程與進程之間沒法共享數據,要想共享數據就得用特殊方法,在主進程建立特殊數據,而後幾個子進程來共享這個主進程的特殊數據
方法一
Array()建立數組,數組,定義數組必需要定義數組的長度,數組裏必須是統一的數據類型【有參】
使用方法:Array('指定數組數據類型',列表樣式的數組元素)
指定數組數據類型有:
'c': ctypes.c_char, 'u': ctypes.c_wchar, 'b': ctypes.c_byte, 'B': ctypes.c_ubyte, 'h': ctypes.c_short, 'H': ctypes.c_ushort, 'i': ctypes.c_int, 'I': ctypes.c_uint, 'l': ctypes.c_long, 'L': ctypes.c_ulong, 'f': ctypes.c_float, 'd': ctypes.c_double
利用Array()數組來多進程共享數據(不推薦使用)
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 temp = multiprocessing.Array('i', [11,22,33,44,]) #建立數組 def Foo(i): #定義函數 #第一條進程,將100加0等於100,從新賦值給數組裏的第0個元素,也就是將數組裏的11改爲了100 #第二條進程,將100加1等於101,從新賦值給數組裏的第1個元素,也就是將數組裏的22改爲了101 temp[i] = 100+i for item in temp: #循環數組 print(i,'----->',item) #循環打印進程線,和數組元素 print("\n") if __name__ == "__main__": #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 for i in range(2): p = multiprocessing.Process(target=Foo,args=(i,)) #建立進程對象 p.start() #激活進程 # 輸出 # 0 -----> 100 # 0 -----> 22 # 0 -----> 33 # 0 -----> 44 # 1 -----> 11 # 1 -----> 101 # 1 -----> 33 # 1 -----> 44
方法二
Manager()建立特殊字典對象【無參】
使用方法:定義變量 = multiprocessing模塊名稱.Manager()
格式:manage = multiprocessing.Manager()
dict()建立特殊字典【可選參數】
參數爲字段值,通常都不設置,爲空便可,注意:這個特殊字典和前面的字典有所區別,但大部分使用方法相同,能夠索引,能夠values()取值
使用方法:殊字典對象變量.dict()
格式:dic = manage.dict()
利用特殊字典dict()來多進程共享數據【推薦】
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 def Foo(i,dic): #定義函數 dic[i] = 100+i #100加以進程線,索引方式從新賦值給特殊字典 print(dic.values()) #打印特殊字典的全部值 if __name__ == '__main__': #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 manage = multiprocessing.Manager() #建立特殊字典對象 dic = manage.dict() #建立特殊字典,值爲空 for i in range(10): #循環建立10條進程 p = multiprocessing.Process(target=Foo,args=(i,dic,)) #建立進程對象 p.start() #激活進程 p.join() #等待一個進程執行完,在執行第二個進程,不然主進程中止了沒法共享數據,由於共享數據時在主進程裏 # 輸出 # [100] # [100, 101] # [100, 101, 102] # [100, 101, 102, 103] # [100, 101, 102, 103, 104] # [100, 101, 102, 103, 104, 105] # [100, 101, 102, 103, 104, 105, 106] # [100, 101, 102, 103, 104, 105, 106, 107] # [100, 101, 102, 103, 104, 105, 106, 107, 108] # [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
進程-隊列-生產者-消費者【不推薦】嚴重耗費內存資源
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 def f2(i,q): #定義生產者函數 q.put("h1") #向隊列裏放數據 def f(i,q): #定義消費者函數 print(i,q.get()) #向列隊裏取數據 if __name__ == '__main__': #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 q = multiprocessing.Queue() #定義對列 for i in range(10): #建立10條進程生產 p = multiprocessing.Process(target=f2, args=(i,q,)) #建立進程對象 p.start() #激活進程 for i in range(10): #建立10條進程消費 p = multiprocessing.Process(target=f, args=(i,q,)) #建立進程對象 p.start() #激活進程 # 輸出 # 1 h1 # 0 h1 # 8 h1 # 5 h1 # 3 h1 # 6 h1 # 7 h1 # 2 h1 # 9 h1 # 4 h1
進程鎖
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 def Foo(lock,temp,i): #建立函數 """ 將第0個數加100 """ lock.acquire() #獲取進程鎖 temp[0] = 100+i #100加上進程線循環次數,從新賦值給進程循環次數對應下標數組裏的值 for item in temp: #循環數組 print(i,'----->',item) #打印出進程循環次數,和數組 lock.release() #釋放進程鎖 print("\n") if __name__ == '__main__': #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 lock = multiprocessing.RLock() #建立進程鎖對象 temp = multiprocessing.Array('i', [11, 22, 33, 44]) #建立數組 for i in range(5): #循環建立5條子進程 p = multiprocessing.Process(target=Foo,args=(lock,temp,i,)) #建立進程對象 p.start() #激活進程 # 輸出 # 0 -----> 100 # 0 -----> 22 # 0 -----> 33 # 0 -----> 44 # 1 -----> 101 # 1 -----> 22 # 1 -----> 33 # 1 -----> 44 # 3 -----> 103 # 3 -----> 22 # 3 -----> 33 # 3 -----> 44 # 2 -----> 102 # 2 -----> 22 # 2 -----> 33 # 2 -----> 44 # 4 -----> 104 # 4 -----> 22 # 4 -----> 33 # 4 -----> 44
進程池
進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,若是進程池序列中沒有可供使用的進進程,那麼程序就會等待,直到進程池中有可用進程爲止。進程池Python有提供
Pool()建立進程池對象【有參】
默認進程池裏沒有進程,只有在向進程池申請進程的時候,進程池才建立進程
使用方法:定義變量 = multiprocessing模塊名稱.Pool(定義進程數)
格式:pool = multiprocessing.Pool(5)
close()進程池裏的進程執行完畢後關閉進程池鏈接【無參】
使用方法:進程池對象變量.close()
格式:pool.close()
terminate()不等進程池裏的進程執行完畢,當即關閉進程池鏈接
使用方法:進程池對象變量.terminate()
格式:pool.terminate()
join()主進程等待進程池裏的子進程所有執行完成後,主進程才中止【可選參數】
可選參數,不寫就是等待直到子進程所有執行完成後,主進程才中止,寫了就是隻等待指定的時間,時間到了就中止主進程,無論子進程有沒有完成
使用方法:進程池對象變量.join(可選參數秒)
格式:pool.join()
向進程池申請進程的方法
apply()向進程池申請一條進程,進程函數執行完後將進程放回進程池,【有參】
注意:apply()向進程池申請的進程不是併發的,是一個進程執行完畢後在執行一個進程,以此循環的,
apply()向進程池申請進程的時候,進程池建立的每個進程都有一個,進程對象.join()方法,因此進程纔是排隊執行的,這裏咱們須要知道一下
使用方法:進程池對象變量.apply(func=要執行的進程函數名稱,args=(執行函數的實際參數、多個參數逗號隔開))
格式:pool.apply(func=Foo,args=(i,))
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 import time #導入時間模塊 def Foo(i): #定義進程執行函數 time.sleep(1) print(i+100) if __name__ == '__main__': #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 pool = multiprocessing.Pool(5) #定義進程池對象 for i in range(10): #循環向進程池申請10條進程 pool.apply(func=Foo,args=(i,)) #向進程池申請進程 # 輸出 # 100 # 101 # 102 # 103 # 104 # 105 # 106 # 107 # 108 # 109
apply_async()向進程池申請一條進程,進程函數執行完後將進程放回進程池,而且能夠設置進程執行函數的回調函數
注意:apply_async()向進程池申請的進程是併發的,也就是申請了幾條進程就是同時執行幾條進程的,回調函數的形式參數、接收的進程執行函數的返回值
apply_async()向進程池申請進程的時候,進程池建立的進程都沒有,進程對象.join()方法,因此進程都是併發的,並且進程對象的daemon=True,也就是主進程不會等待子進程執行完畢就終止,因此使用apply_async()向進程池申請進程的時候,進程申請後,要使用close()進程池裏的進程執行完畢後關閉進程池鏈接,join()主進程等待進程池裏的子進程所有執行完成後,主進程才中止,不然會報錯
使用方法:進程池對象變量.apply_async(func=要執行的進程函數名稱,args=(執行函數的實際參數、多個參數逗號隔開),callback=回調函數名稱)
格式:pool.apply_async(func=Foo,args=(i,),callback=f2)
#!/usr/bin/env python # -*- coding:utf8 -*- import multiprocessing #導入進程模塊 import time #導入時間模塊 def Foo(i): #定義進程執行函數 time.sleep(1) print(i+100) return "返回值,返回給回調函數的,形式參數" def f2(a): #執行函數的回調函數,形式參數等於執行函數的返回值 print(a) #打印進程執行函數返回的值 if __name__ == '__main__': #wds系統下必須if __name__ == "__main__"才能建立進程,咱們調試不要緊,之後在Linux系統沒這個問題 pool = multiprocessing.Pool(5) #定義進程池對象 for i in range(10): #循環向進程池申請10條進程 pool.apply_async(func=Foo,args=(i,),callback=f2) #向進程池申請進程,並設置執行函數,和回調函數 pool.close() #進程池裏的進程執行完畢後關閉進程池鏈接 pool.join()#主進程等待進程池裏的子進程所有執行完成後,主進程才中止 # 輸出 # 100 # 返回值,返回給回調函數的,形式參數 # 101 # 返回值,返回給回調函數的,形式參數 # 102 # 返回值,返回給回調函數的,形式參數 # 103 # 返回值,返回給回調函數的,形式參數 # 104 # 返回值,返回給回調函數的,形式參數 # 105 # 返回值,返回給回調函數的,形式參數 # 106 # 返回值,返回給回調函數的,形式參數 # 107 # 返回值,返回給回調函數的,形式參數 # 108 # 返回值,返回給回調函數的,形式參數 # 109 # 返回值,返回給回調函數的,形式參數
apply_async()向進程池申請進程原理圖
自定義線程池
自定義線程池版本一
這個版本並不理想,可是簡單
#!/usr/bin/env python # -*- coding:utf8 -*- import queue #導入隊列模塊 import threading #導入線程模塊 """定義一個類""" class ThreadPool(object): #建立類 def __init__(self, max_num=20): #初始化 self.queue = queue.Queue(max_num) #定義普通字段等於,長度爲20的隊列 for i in range(max_num): #設置20次循環 self.queue.put(threading.Thread) #循環向隊列裏,放入20個線程對象名稱 def get_thread(self): #定義get_thread方法 return self.queue.get() #返回在隊列裏取出線程名稱 def add_thread(self): #定義add_thread方法 self.queue.put(threading.Thread) #向隊列裏放入一個線程對象名稱 """建立一個類對象""" pool = ThreadPool(20) #建立類對象,初始化__init__方法 """定義線程執行函數""" def func(arg, p): #定義線程執行函數 print(arg) #打印線程數,也就是第幾回循環線程 import time #導入時間模塊 time.sleep(2) #睡眠2秒 p.add_thread() #向隊列放入一個線程對象名稱,建立一個線程對象的時候,就從隊列裏拿走一個線程對象名稱,全部要在放回一個回去 """建立線程""" for i in range(30): #定義一個30次循環 thread = pool.get_thread() #在列隊裏拿出一個線程名稱 t = thread(target=func, args=(i, pool)) #在隊列裏拿出一個線程對象名稱,建立一個線程對象,傳入線程執行函數和參數 t.start() #激活線程 # 輸出 # 0 # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10 # 11 # 12 # 13 # 14 # 15 # 16 # 17 # 18 # 19 # 20 # 21 # 22 # 23 # 24 # 25 # 26 # 27 # 28 # 29
自定義線程池版本一原理圖
自定義線程池版本二【推薦使用】
ThreadPool源碼模塊,使用方法將ThreadPool源碼模塊文件,放到工程目錄下,導入模塊使用
#!/usr/bin/env python # -*- coding:utf8 -*- """線程池源碼""" import queue #導入隊列模塊 import threading #導入線程模塊 import contextlib #導入上下文管理模塊 StopEvent = object() #設置全局變量,中止標誌 class ThreadPool(object): #建立類 """ ThreadPool()建立線程池類對象,有參:線程最大數量【使用方法:定義線程池對象變量 = ThreadPool(線程最大數量)】 run()向線程池申請一條線程,有參【使用方法:線程對象.run(線程執行函數,(執行函數參數),回調函數)】 close()讓全部線程執行完畢後,中止線程,無參【使用方法:線程對象.close()】 terminate()不管是否還有任務,終止線程,有參:是否當即清空隊列裏的數據,默認yes清空,no不清空【使用方法:線程對象.terminate(yes或no)】 """ def __init__(self, max_num, max_task_num = None): """ 初始化ThreadPool類數據,建立隊列,記錄線程最大數量,建立、記錄真實建立的線程列表 建立、記錄空閒線程列表 """ if max_task_num: #判斷max_task_num若是有值 self.q = queue.Queue(max_task_num) #建立隊列,隊列長度爲max_task_num的值 else: #若是max_task_num沒有值 self.q = queue.Queue() #建立隊列,隊列的長度沒有限制 self.max_num = max_num #建立普通字段max_num等於,定義ThreadPool類對象的第一個實際參數,也就是最多能建立的線程數 #self.cancel = False #建立普通字段cancel = False self.terminal = False #建立普通字段terminal = False,以這個標識決定線程是否繼續到隊列取任務 self.generate_list = [] #建立generate_list空列表,記錄真實建立的線程 self.free_list = [] #建立free_list空列表,記錄空閒線程 def run(self, func, args, callback=None): w = (func, args, callback,) #將傳進來的,線程執行函數名稱和執行函數參數,以及回調函數名稱,組合成一個元組賦值給w變量 self.q.put(w) #將元祖放入對列中 """判斷空閒線程列表等於0,也就是空閒列表裏沒有空閒的線程時, 而且真實建立的線程列表裏的線程數小於總線程數,執行generate_thread方法 """ if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: self.generate_thread() #執行generate_thread方法 def generate_thread(self): t = threading.Thread(target=self.call) #建立一個線程,線程執行函數爲call方法 t.start() #激活線程 def call(self): """ 循環去獲取任務函數並執行任務函數 """ current_thread = threading.currentThread() #獲取當前線程 self.generate_list.append(current_thread) #將獲取到的當前線程,追加到真實建立的線程列表裏 event = self.q.get() #到隊列裏取出,run方法放入隊列的元組 while event != StopEvent: #若是到隊列裏取到的不等於中止標誌,說明是元組,若是是元組開始循環 """將元組裏的3個元素,分別賦值給3個變量,第一個是線程執行函數名稱,第二個是線程執行函數參數,第三個是回調函數名稱""" func, arguments, callback = event #success = True #自定義一個執行函數是否執行成功標識,默認True表示成功 try: result = func(*arguments) #執行線程執行函數,並接收執行函數參數 except Exception as e: #若是執行錯誤 result = e #若是線程執行函數出錯,線程執行函數返回值等於錯誤信息 if callback is not None: #若是回調函數存在 try: callback(result) #執行回調函數,並將執行函數返回結果傳值給回調函數的形式參數result except Exception as e: pass """標記線程空閒了""" if self.terminal: #判斷terminal變量是True event = StopEvent #若是是True就想列隊裏放入線程中止標誌 else: with self.worker_state(self.free_list, current_thread): #執行裏面代碼塊前先執行上下文管理函數 event = self.q.get() #到隊列裏取出,run方法放入隊列的元組,沒有就等待 else: self.generate_list.remove(current_thread) #若是在隊列裏取出的不是元組,而是中止標識,就在真實建立的線程列表裏移除當前的線程 def close(self): """ 執行完全部的任務後,全部線程中止 """ full_size = len(self.generate_list) #獲取真實建立的線程列表裏的線程個數 while full_size: #循環,真實建立線程列表裏,的線程個數對應的次數 self.q.put(StopEvent) #每循環一次,向隊列里加一個全局變量StopEvent,中止標識 full_size -= 1 #每循環一次讓循環次數減一 def terminate(self, qkdl = "yes"): """ 不管是否還有任務,終止線程 """ if qkdl == "yes": self.terminal = True #將是否繼續到隊列取任務的判斷變量修改成True,向隊列裏放中止標識,使其線程中止 self.q.empty() #清空隊列裏的全部數據 zuiduo = len(self.generate_list) #檢查真實建立線程列表裏有多少個線程 while zuiduo: #循環真實建立線程列表裏線程數,對應次數 self.q.put(StopEvent) #每循環一次向隊列裏放中止標識 zuiduo -= 1 #每循環一次,減小一次循環次數 else: self.terminal = True #將是否繼續到隊列取任務的判斷變量修改成True,向隊列裏放中止標識,使其線程中止 zuiduo = len(self.generate_list) #檢查真實建立線程列表裏有多少個線程 while zuiduo: #循環真實建立線程列表裏線程數,對應次數 self.q.put(StopEvent) #每循環一次向隊列裏放中止標識 zuiduo -= 1 #每循環一次,減小一次循環次數 @contextlib.contextmanager #定義上下文管理裝飾器 def worker_state(self, state_list, worker_thread): #定義上下文管理裝飾器函數 """ 用於記錄線程中正在等待的線程數 """ state_list.append(worker_thread) #執行代碼塊前執行,將當前線程追加到空閒線程列表 try: yield #遇到yield,跳出裝飾器函數,執行代碼塊後,在回到yield這裏向下執行 finally: state_list.remove(worker_thread) #執行代碼塊後執行,將當前線程移除空閒線程列表
ThreadPool自定義線程池版本二模塊使用說明
首先from xxx import ThreadPool 導入模塊
ThreadPool()建立線程池對象【有參】
使用方法:定義線程池對象變量 = ThreadPool模塊名稱.ThreadPool(線程池線程最大數量)
格式:pool = ThreadPool.ThreadPool(5)
run()到線程池申請一條線程【有參】
使用方法:線程池對象.run(線程執行函數,(線程執行函數的參數),回調函數)
格式:pool.run(f1,(i,),f2)
close()執行完全部的任務後,全部線程中止【無參】
使用方法:線程池對象.close()
格式:pool.close()
terminate()不管是否還有任務,終止線程【可選參數】
使用方法:線程池對象.terminate()
參數默認爲yes終止線程前清空隊列,no爲終止線程不清空隊列
格式:pool.terminate()
ThreadPool自定義線程池版本二使用代碼
#!/usr/bin/env python # -*- coding:utf8 -*- from lib.ska import ThreadPool #導入線程池模塊 import time #導入時間模塊 def f2(i): #定義回調函數 print(i) #打印線程執行函數的返回值,回調函數的形式參數接收的,線程執行函數的返回值 def f1(i): #定義線程執行函數 time.sleep(1) #睡眠1秒 print(i) #打印申請線程時傳進來的參數 return "回調" #返回值給回調函數 pool = ThreadPool.ThreadPool(5) #建立線程池對象 for i in range(100): #循環 pool.run(f1,(i,),f2) #到線程池申請線程 pool.close() #執行完全部的任務後,全部線程中止 #pool.terminate() #不管是否還有任務,終止線程
自定義線程池版本二原理圖
協程
協程又叫(微線程),就是在一個線程裏能夠建立多個協程,由程序員來控制何時執行那條協程,協程能夠用一條線程,同時執行多個任務,適用於IO密集型場景
協程存在的意義:對於多線程應用,CPU經過切片的方式來切換線程間的執行,線程切換時須要耗時(保存狀態,下次繼續)。協程,則只使用一個線程,在一個線程中規定某個代碼塊執行順序。
協程的適用場景:當程序中存在大量不須要CPU的操做時(IO),適用於協程;
greenlet最基礎協程模塊 第三方模塊
greenlet()建立協程對象【有參】
使用方法:自定義變量 = greenlet(協程執行函數)
格式:gr1 = greenlet(test1)
switch()執行指定的協程,switch()前面爲指定要執行的協程對象名稱【無參】
若是協程執行函數裏,遇到switch()時就會跳出當前協程執行函數,並記錄當前跳出位置,去執行遇到switch()指定的線程,記錄的跳出位置下次進入時,從跳出位置開始
使用方法:要執行的協程對象變量.switch()
格式:gr1.switch()
簡單協程代碼
#!/usr/bin/env python # -*- coding:utf8 -*- from greenlet import greenlet #導入協程模塊 def test1(): #定義協程執行函數 print(12) #打印12 gr2.switch() #執行指定的協程,switch()前面爲指定要執行的協程對象名稱,執行gr2協程 print(34) #打印34 gr2.switch() #執行指定的協程,switch()前面爲指定要執行的協程對象名稱,執行gr1協程 def test2(): #定義協程執行函數 print(56) #打印56 gr1.switch() #執行指定的協程,switch()前面爲指定要執行的協程對象名稱,執行gr1協程 print(78) #打印78 gr1 = greenlet(test1) #建立協程對象,傳入協程執行函數 gr2 = greenlet(test2) #建立協程對象,傳入協程執行函數 gr1.switch() #執行指定的協程,switch()前面爲指定要執行的協程對象名稱,執行gr1協程
簡單協程原理圖
gevent協程模塊
gevent協程模塊是基於greenlet模塊改進的,也是第三方模塊
joinall()建立協程對象【有參】
參數是列表類型的,建立協程spawn()方法
使用方法:模塊名稱.joinall([gevent.spawn(線程執行函數)])
格式:gevent.joinall([gevent.spawn(foo), gevent.spawn(bar), gevent.spawn(ba),])
spawn()建立協程【有參】
參數是協程執行函數名稱
使用方法:gevent模塊名稱.joinall([gevent.spawn(協程執行函數名稱), gevent.spawn(協程執行函數名稱), gevent.spawn(協程執行函數名稱),])
格式:gevent.joinall([gevent.spawn(foo), gevent.spawn(bar), gevent.spawn(ba),])
sleep()跳出協程執行函數,執行協程對象裏的,下一個協程,並記錄當前跳出位置,再次進入當前協程執行函數時,從當前跳出位置開始
使用方法:模塊名稱.sleep(0)
格式:gevent.sleep(0)
gevent簡單協程代碼
#!/usr/bin/env python # -*- coding:utf8 -*- import gevent #導入協程模塊 def foo(): #定義協程執行函數 print(12) gevent.sleep(0) #執行協程對象裏下一條協程,若是已是最後一條協程,就返回第一條協程執行 print(34) def bar(): #定義協程執行函數 print(56) gevent.sleep(0) #執行協程對象裏下一條協程,若是已是最後一條協程,就返回第一條協程執行 print(78) def ba(): #定義協程執行函數 print(910) gevent.sleep(0) #執行協程對象裏下一條協程,若是已是最後一條協程,就返回第一條協程執行 print(1112) gevent.joinall([ #定義協程對象 gevent.spawn(foo), #建立協程 gevent.spawn(bar), #建立協程 gevent.spawn(ba), #建立協程 ]) # 輸出 # 12 # 56 # 910 # 34 # 78 # 1112
gevent簡單協程原理圖
遇到IO操做自動切換:
#!/usr/bin/env python # -*- coding:utf8 -*- from gevent import monkey; monkey.patch_all() #導入模塊目錄裏的,gevent目錄,裏的monkey模塊的patch_all()方法 import gevent #導入協程模塊 import requests #模擬瀏覽器請求模塊 def f(url): #建立協程執行函數 print('GET: %s' % url) #打印字符串格式化GET:+函數參數url resp = requests.get(url) #將url發送http請求 data = resp.text #獲取http字符串代碼 print('%d 請求返回 %s.' % (len(data), url)) #打印字符串格式化,http字符串代碼字符串數和url地址 """ 至關於三條協程同時發url請求,那條協程先完成請求就先獲取那條協程的返回數據 也就是,協程在發IO請求時不會等待發送的請求返回數據完成,就自動切換下一條線程開始發下一條請求了,全部協程請求發完後,那條請求先返回數據完成,就先獲取那條請求的數據 """ gevent.joinall([ #建立協程對象 gevent.spawn(f, 'https://www.python.org/'), #建立協程,傳入執行函數和執行函數參數 gevent.spawn(f, 'https://www.yahoo.com/'), #建立協程,傳入執行函數和執行函數參數 gevent.spawn(f, 'https://github.com/'), #建立協程,傳入執行函數和執行函數參數 ])
遇到IO操做自動切換原理圖