第五十七節,線程、進程、協程

線程python

首先弄清進程和線程之間的區別,這一點是很是重要的。線程與進程的不一樣之處在於,它們共享狀態、內存和資源。對於線程來講,這個簡單的區別既是它的優點,又是它的缺點。一方面,線程是輕量級的,而且相互之間易於通訊,但另外一方面,它們也帶來了包括死鎖、爭用條件和高複雜性在內的各類問題。幸運的是,因爲 GIL 和隊列模塊,與採用其餘的語言相比,採用 Python 語言在線程實現的複雜性上要低得多。不管是建立進程或者線程都是爲了實現併發操做git

 

Python進程、線程之間的原理圖程序員

 

計算機有進程和線程的目的:提升執行效率
計算機默認有主進程和主線程github

進程:
  優勢:同時利用多個CPU,可以同時進行多個操做
  缺點:耗費資源(從新開闢內存空間)
  進程不是越多越好,理論上CPU個數(核數)=進程個數
  計算密集型適用於進程,由於計算之類的須要CPU運算(佔用CPU)
線程:
  優勢:共享內存,IO操做時,創造併發操做
  缺點:槍戰資源
  線程不是越多越好,具體案例具體分析,請求上下文切換耗時
  IO密集型適用於線程,IO操做打開文件網絡通信類,不須要佔用CPU,只是由CPU調度一下(不佔用CPU)算法

自定義進程和線程:注意python解釋器自帶了主進程和主線程,好比在代碼文件裏沒有定義線程和進程,程序也能運行就是靠的解釋器自帶主進程的主線程執行的api

  自定義進程:
    由主進程建立,子進程
  自定義線程:
    由主程建立,子線程數組

GIL全局解釋器鎖:瀏覽器

GIL全局解釋器鎖在進程入口,控制着進程數量與CPU的相應安全

 

threading線程模塊

線程是應用程序中工做的最小單元網絡

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()

 

線程鎖threading.RLock和threading.Lock

 咱們使用線程對數據進行操做的時候,若是多個線程同時修改某個數據,可能會出現不可預料的結果,爲了保證數據的準確性,引入了鎖的概念。

沒有線程鎖的狀況列如:一個全局變量值爲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

 

threading.RLock和threading.Lock 的區別

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()

 

threading.Event事件對象

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的時候,線程不會阻塞。它提供了本地和遠程的併發性。

Event事件對象的方法有

  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.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操做自動切換原理圖

相關文章
相關標籤/搜索