Python之協程技術

2021-10-03 07:15:03 字數 4295 閱讀 3452

1.定義:

纖程,微執行緒。是允許在不同入口點不同位置暫停或開始的電腦程式,簡單來說,協程就是可以暫停執行的函式。

2. 協程原理 :

記錄乙個函式的上下文,協程排程切換時會將記錄的上下文儲存,在切換回來時進行調取,恢復原有的執行內容,以便從上一次執行位置繼續執行。

協程本質上就是乙個執行緒,以前多執行緒任務的切換是由作業系統控制的,遇到i/o阻塞就自動切換,現在我們用協程的目的就是較少作業系統切換的開銷(開關執行緒,建立暫存器、堆疊等,在他們之間進行切換等),在我們自己的程式(應用層面)裡面來控制任務的切換。對於單執行緒下,我們不可避免程式**現io操作,但如果我們能在自己的程式中(即使用者程式級別,而非作業系統級別)控制單執行緒下的多個任務能在乙個任務遇到io阻塞時就切換到另外乙個任務去計算,這樣就保證了該執行緒能夠最大限度地處於就緒態,即隨時都可以被cpu執行的狀態,相當於我們在使用者程式級別將自己的io操作最大限度地隱藏起來,從而可以迷惑作業系統,讓其看到:該執行緒好像是一直在計算,io比較少,從而更多的將cpu的執行許可權分配給我們的執行緒。

3.

協程優缺點

優點

協程完成多工占用計算資源很少

由於協程的多工切換在應用層完成,因此切換開銷少,最大限度地利用cpu

協程為單執行緒程式,無需進行共享資源同步互斥處理

缺點 協程的本質是乙個單執行緒,無法利用計算機多核資源

python3.5

以後,使用標準庫

asyncio

和async/await

語法來編寫併發**。

asyncio

庫通過對非同步

io行為的支援完成python

的協程。雖然官方說asyncio

是未來的開發方向,但是由於其生態不夠豐富,大量的客戶端不支援awaitable

需要自己去封裝,所以在使用上存在缺陷。更多時候只能使用已有的非同步庫(asyncio

等),功能有限。

示例:

import asyncio

async def fun1():

print("start1")

# 遇到阻塞跳出

await asyncio.sleep(3)

print("end1")

async def fun2():

print("start2")

# 遇到阻塞跳出

await asyncio.sleep(2)

print("end2")

cor1 = fun1()

cor2 = fun2()

tasks = [asyncio.ensure_future(cor1),

asyncio.ensure_future(cor2)]

loop = asyncio.get_event_loop()

loop.run_until_complete(asyncio.wait(tasks))

# start1

# start2

# end2

# end1

安裝 :sudo pip3 install greenlet

函式:

g = greenlet.greenlet(func)

功能:建立協程物件

引數:協程函式

返回值:greenlet 協程函式物件

g.switch()

功能:選擇要執行的協程函式

示例:

"""

協程行為展示

"""from greenlet import greenlet

def fun1():

print("執行fun1")

# 跳轉執行fun2

gr2.switch()

print("結束fun1")

# 跳回fun2繼續往下執行,而不是從fun2第一行開始

gr2.switch()

def fun2():

print("執行fun2")

# 跳回fun1繼續往下執行,而不是從fun1第一行開始

gr1.switch()

print("結束fun2")

# 將函式變為協成函式

gr1 = greenlet(fun1)

gr2 = greenlet(fun2)

gr1.switch()

安裝:sudo pip3 install gevent

函式:

gevent.spawn(func,ar**)

功能: 生成協程物件

引數:func

協程函式

ar**

給協程函式傳參(不定參)

返回值: 協程物件

gevent.joinall(

list

,[timeout])

功能: 阻塞等待協程執行完畢

引數:list

協程物件列表

timeout

超時時間

gevent.sleep(sec)

功能: gevent

睡眠阻塞

引數:睡眠時間

* gevent協程只有在遇到gevent指定的阻塞行為時才會自動在協程之間進行跳轉,如gevent.joinall(),gevent.sleep()帶來的阻塞

monkey指令碼

作用:

在gevent

協程中,協程只有遇到

gevent

指定型別的阻塞才能跳轉到其他協程,因此,我們希望將普通的io

阻塞行為轉換為可以觸發

gevent

協程跳轉的阻塞,以提高執行效率。

轉換方法:

gevent

提供了乙個指令碼程式

monkey,

可以修改底層解釋

io阻塞的行為,將很多普通阻塞轉換為gevent

阻塞。  

使用方法:

1】 匯入

monkey

from gevent import monkey

【2】 執行相應的指令碼,例如轉換

socket

中所有阻塞

monkey.patch_socket()

【3】 如果將所有可轉換的

io阻塞全部轉換則執行

all

monkey.patch_all()

【4】 注意:指令碼執行函式需要在對應模組匯入前執行

示例:

"""

基於協程的tcp併發

"""import gevent

from gevent import monkey

monkey.patch_socket() # 執行指令碼修改socket阻塞行為

from socket import *

def handle(c):

while true:

data = c.recv(1024).decode() # 屬於協程阻塞,不影響其他客戶端

if not data:

return

print(data)

c.send(b'ok')

# 建立套接字

s = socket()

s.setsockopt(sol_socket, so_reuseaddr, 1)

s.bind(('127.0.0.1', 8888))

s.listen(5)

# 迴圈接收來自客戶端請求

while true:

c, addr = s.accept()

print("connect from", addr)

# handle(c) # 迴圈方案

gevent.spawn(handle, c) # 協程方案

python之協程建立

協程,是充分利用cpu給該執行緒的時間,在乙個執行緒裡放置多個任務,當某個任務阻塞時就執行下乙個任務。其特點是能夠記住這些任務執行到哪一步了,下次再執行該任務的時候回從上次阻塞的地方繼續開始。建立協程需要使用gevent模組。如下 import gevent gevent協程中,遇到需要耗費時間的操...

Python 高階 之 協程

協程的概念級描述 與執行緒對比 知乎 鏈結 執行緒有兩個必須要處理的問題 一是碰著阻塞式i o會導致整個程序被掛起 二是由於缺乏時鐘阻塞,程序需要自己擁有排程執行緒的能力。如果一種實現使得每個執行緒需要自己通過呼叫某個方法,主動交出控制權。那麼我們就稱這種執行緒是協作式的,即是協程。在python中...

Python學習之協程

我們都知道執行緒間的任務切換是由作業系統來控制的,而協程的出現,就是為了減少作業系統的開銷,由協程來自己控制任務的切換 協程本質上就是執行緒。既然能夠切換任務,所以執行緒有兩個最基本的功能 一是儲存狀態 二是任務切換 8.8.1 協程的特點 優點 缺點 特點 8.8.2 greenlet 使用gre...