[Python 多線程] asyncio (十六)


 

asyncio

 

該模塊是3.4版本加入的新功能。

 

先來看一個例子:

def a():
    for x in range(3):
        print('a.x', x)

def b():
    for x in 'abc':
        print('b.x', x)

a()
b()

#運行結果:
a.x 0
a.x 1
a.x 2
b.x a
b.x b
b.x c

  這個例子是一個典型的串行程序,兩個函數調用是在主線程中順序執行。

 

有以下幾種方法可以讓這段程序改為並行:

1. 生成器

2. 多線程

3. 多進程

4. 協程

 

1)生成器方法:

def a():
    for x in range(3):
        yield x


def b():
    for x in 'abc':
        yield x


m = a()
n = b()

for _ in range(3):
    print(next(m))
    print(next(n))

#運行結果:
0
a
1
b
2
c

  使用生成器來實現交替執行。這兩個函數都有機會執行,這樣的調度不是操作系統的進程、線程完成的,而是用戶自己設計的。

 

2)多線程方法:

import threading,time

def a():
    for x in range(3):
        time.sleep(0.0001)
        print('a.x',x)

def b():
    for x in 'abc':
        time.sleep(0.0001)
        print('b.x',x)

threading.Thread(target=a).start()
threading.Thread(target=b).start()

#運行結果:
a.x 0
b.x a
a.x 1
b.x b
a.x 2
b.x c

  主要使用sleep函數強制切換來實現偽並行。

 

3)多進程方式:

import multiprocessing

def a():
    for x in range(3):
        print('a.x',x)

def b():
    for x in 'abc':
        print('b.x',x)

if __name__ == '__main__':
    multiprocessing.Process(target=a).start()
    multiprocessing.Process(target=b).start()

#運行結果:
a.x 0
a.x 1
a.x 2
b.x a
b.x b
b.x c

  多進程方式才是真正的並行。

 

4)協程方法:

 

協程,需要使用到 asyncio 標准庫,是Python3.4版本加入的新功能,底層基於selectors實現,包括異步IO、事件循環、協程等內容。

 

事件循環:

事件循環是asyncio提供的核心運行機制。

程序開啟一個無限的循環,使用者會把一些函數注冊到事件循環上。當滿足事件發生的時候,調用相應的協程函數。

 

4.1 事件循環基類

asyncio.BaseEventLoop  這個類是一個實現細節,它是asyncio.AbstractEventLoop的子類,不可以直接使用
asyncio.AbstractEventLoop  事件循環的抽象基類,這個類是是線程不安全的

 

4.2 運行事件循環

asyncio.get_event_loop()  返回一個事件循環對象,是asyncio.BaseEventLoop的實例
asyncio.AbstractEventLoop.stop()  停止運行事件循環
asyncio.AbstractEventLoop.run_forever()  一直運行,直到調用stop()
asyncio.AbstractEventLoop.run_until_complete(future)  運行直到future對象運行完成,返回結果
asyncio.AbstractEventLoop.close()  關閉事件循環
asyncio.AbstractEventLoop.is_running()  返回事件循環的運行狀態
asyncio.AbstractEventLoop.is_closed()  如果事件循環已關閉,返回True

 

4.3 協程

協程不是進程、也不是線程,它是用戶空間調度完成並發處理的方式。(同一線程內交替執行其實也是偽並發)

並發指的是在一段時間內做了多少、並行指的是同一時刻有多少同時執行。

進程、線程由操作系統完成調度,而協程是線程內完成調度。它不需要更多的線程,也就沒有多線程切換帶來的開銷。

協程是非搶占式調度,只有一個協程主動讓出控制權,另一個協程才會被調度。

協程也不需要鎖機制,因為是在同一線程中執行。

多CPU下,可以使用多線程和協程配合,既能進程並發又能發揮協程在單線程中的優勢。

Python中協程是基於生成器的。

 

4.4 協程的使用

4.4.1 Python3.4中使用@asyncio.coroutine 、 yield from

#asyncio Python3.4

import asyncio

@asyncio.coroutine
def foo(x): #生成器函數上面加了協程裝飾器之后就轉化成協程函數
	for i in range(3):
		print('foo {}'.format(i))
		yield from asyncio.sleep(x) #調用另一個生成器對象

loop = asyncio.get_event_loop() #獲得一個時間循環
loop.run_until_complete(foo(1)) #傳入一個生成器對象的調用
loop.close()


#運行結果:
foo 0
foo 1
foo 2
[Finished in 3.3s]

  此例子在一個生成器函數加了協程裝飾器之后,該生成器函數就轉化成了協程函數。

 

4.4.2 Python3.5中使用關鍵字 async def 、 await ,在語法上原生支持協程

#asyncio Python3.5

import asyncio

async def foo(x):  #異步定義,協程定義
	for i in range(3):
		print('foo {}'.format(i))
		await asyncio.sleep(x) #不可以出現yield,使用await替換

print(asyncio.iscoroutinefunction(foo))
loop = asyncio.get_event_loop()
loop.run_until_complete(foo(1)) #傳入一個協程對象的調用
loop.close()

#運行結果:
True
foo 0
foo 1
foo 2
[Finished in 3.3s]

  async def 用來定義協程函數,iscoroutinefunction(func)判斷func函數是否是一個協程函數。協程函數中可以不包含await、async關鍵字,但不能使用yield關鍵字。

 

其它語法:async with,支持上下文的協程

 

4.4.3 coroutine asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

等待futures序列中的協程對象執行完成,futures序列不可以為空。

timeout可以用於控制返回前等待的最大秒數,秒數可以是int或浮點數,如果未指定timeout,則無限制。

#wait多個協程對象
import asyncio

@asyncio.coroutine
def a():
	for i in range(3):
		print('a.x',i)
		yield

@asyncio.coroutine
def b():
	for i in range(3):
		print('b.x',i)
		yield


loop = asyncio.get_event_loop()
tasks = [a(),b()]
loop.run_until_complete(asyncio.wait(tasks)) #傳入一個協程對象序列

loop.close()


#運行結果:
b.x 0
a.x 0
b.x 1
a.x 1
b.x 2
a.x 2
[Finished in 0.3s]

  

總結:

傳統的多線程、多進程都是系統完成調度,而協程是在進程中的線程內由用戶空間調度完成並發處理,主要依靠生成器來實現交替調度。

Python3.4中使用@asyncio.coroutine、yield from調用另一個生成器對象

Python3.5中使用關鍵字 async def 和 await,且不可以出現yield關鍵字。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM