Python Language
Asyncioモジュール
サーチ…
コルーチンと委譲の構文
Python 3.5+がリリースされる前に、 asyncio
モジュールはジェネレータを使用して非同期呼び出しを模倣し、現在のPython 3.5リリースとは異なる構文を持っていました。
Python 3.5では、 async
キーワードとawait
キーワードが導入されました。 await func()
呼び出しのawait func()
にかっこがないことに注意してください。
import asyncio
async def main():
print(await func())
async def func():
# Do time intensive stuff...
return "Hello, world!"
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Python 3.5より前には、 @asyncio.coroutine
デコレータを使ってコルーチンを定義していました。表現からの歩留まりは、ジェネレータの委任に使用されました。 yield from func()
まわりのかっこに注意してください。
import asyncio
@asyncio.coroutine
def main():
print((yield from func()))
@asyncio.coroutine
def func():
# Do time intensive stuff..
return "Hello, world!"
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
2つの関数を非同期で実行する方法を示す例を次に示します。
import asyncio
async def cor1():
print("cor1 start")
for i in range(10):
await asyncio.sleep(1.5)
print("cor1", i)
async def cor2():
print("cor2 start")
for i in range(15):
await asyncio.sleep(1)
print("cor2", i)
loop = asyncio.get_event_loop()
cors = asyncio.wait([cor1(), cor2()])
loop.run_until_complete(cors)
非同期エグゼキュータ
注:Python 3.5以上のasync / await構文を使用します
asyncio
は、タスクを非同期にスケジュールするためにconcurrent.futures
にあるExecutor
オブジェクトの使用をサポートしています。イベントループは、 Executor
オブジェクト、 Callable
、およびCallableのパラメータをとるrun_in_executor()
関数を持っています。
以下のためのタスクのスケジュールExecutor
import asyncio
from concurrent.futures import ThreadPoolExecutor
def func(a, b):
# Do time intensive stuff...
return a + b
async def main(loop):
executor = ThreadPoolExecutor()
result = await loop.run_in_executor(executor, func, "Hello,", " world!")
print(result)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
各イベントループはまた、「デフォルト」の持つExecutor
に割り当て可能なスロットExecutor
。 Executor
を割り当て、ループからタスクをスケジュールするには、 set_default_executor()
メソッドを使用します。
import asyncio
from concurrent.futures import ThreadPoolExecutor
def func(a, b):
# Do time intensive stuff...
return a + b
async def main(loop):
# NOTE: Using `None` as the first parameter designates the `default` Executor.
result = await loop.run_in_executor(None, func, "Hello,", " world!")
print(result)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.set_default_executor(ThreadPoolExecutor())
loop.run_until_complete(main(loop))
concurrent.futures
には2つの主なタイプのExecutor
、 ThreadPoolExecutor
とProcessPoolExecutor
ます。 ThreadPoolExecutor
は、スレッドのプールが含まれています。スレッドのプールは、コンストラクタを使用して特定の数のスレッドに手動で設定することも、マシンのコア数の5倍にすることもできますThreadPoolExecutor
はスレッドプールを使用して割り当てられたタスクを実行します。 I / Oバウンド操作ではなくCPUバウンド操作で一般的に優れています。 ProcessPoolExecutor
とは対照的に、割り当てられた各タスクに対して新しいプロセスを生成します。 ProcessPoolExecutor
は、picklableであるタスクとパラメータしか取ることができません。最も一般的な非pickableタスクは、オブジェクトのメソッドです。 Executor
タスクとしてオブジェクトのメソッドをスケジュールする必要がある場合は、 ThreadPoolExecutor
使用する必要があります。
UVLoopの使用
uvloop
は、libuvに基づいたasyncio.AbstractEventLoop
実装です(nodejsによって使用されます)。 99%のasyncio
機能に準拠しており、従来のasyncio.EventLoop
よりもはるかに高速asyncio.EventLoop
。現在、Windowsではuvloop
は利用できませんpip install uvloop
。
import asyncio
import uvloop
if __name__ == "__main__":
asyncio.set_event_loop(uvloop.new_event_loop())
# Do your stuff here ...
一つは、また、設定することで、イベントループの工場を変更することができますEventLoopPolicy
1つにuvloop
。
import asyncio
import uvloop
if __name__ == "__main__":
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
loop = asyncio.new_event_loop()
同期プリミティブ:イベント
概念
複数のコルーチンのスケジューリングを同期するには、 Event
を使用します。
簡単に言えば、イベントは走っているレースで撮影された銃のようなものです。ランナーを出発ブロックから離れることができます。
例
import asyncio
# event trigger function
def trigger(event):
print('EVENT SET')
event.set() # wake up coroutines waiting
# event consumers
async def consumer_a(event):
consumer_name = 'Consumer A'
print('{} waiting'.format(consumer_name))
await event.wait()
print('{} triggered'.format(consumer_name))
async def consumer_b(event):
consumer_name = 'Consumer B'
print('{} waiting'.format(consumer_name))
await event.wait()
print('{} triggered'.format(consumer_name))
# event
event = asyncio.Event()
# wrap coroutines in one future
main_future = asyncio.wait([consumer_a(event),
consumer_b(event)])
# event loop
event_loop = asyncio.get_event_loop()
event_loop.call_later(0.1, functools.partial(trigger, event)) # trigger event in 0.1 sec
# complete main_future
done, pending = event_loop.run_until_complete(main_future)
出力:
消費者Bは待っている
消費者は待っている
EVENT SET
消費者Bがトリガーされた
コンシューマAがトリガされた
シンプルなWebSocket
ここでは、 asyncio
を使って単純なエコーwebsocketをasyncio
ます。サーバーに接続してメッセージを送受信するためのコルーチンを定義します。 websocketの通信は、イベントループによって実行されるmain
コルーチンで実行されます。この例は、 以前の投稿から変更されています。
import asyncio
import aiohttp
session = aiohttp.ClientSession() # handles the context manager
class EchoWebsocket:
async def connect(self):
self.websocket = await session.ws_connect("wss://echo.websocket.org")
async def send(self, message):
self.websocket.send_str(message)
async def receive(self):
result = (await self.websocket.receive())
return result.data
async def main():
echo = EchoWebsocket()
await echo.connect()
await echo.send("Hello World!")
print(await echo.receive()) # "Hello World!"
if __name__ == '__main__':
# The main loop
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
asyncioに関する一般的な誤解
おそらくについての最も一般的な誤解asnycio
(別のスレッドで)並列に遮断するジョブを実行するため、GIL(グローバルインタプリタロック)をsidesteppingと-それはあなたが並行して任意のタスクを実行できることです。それはしない !
asyncio
(およびasyncio
と共同作業するために構築されたasyncio
)は、コルーチン上に構築されています。上の例ではasyncio.sleep
に注意してください。これは、バックグラウンドで待っている非ブロッキングコルーチンの例であり、コールバック関数( await
呼び出されたとき)に制御フローを戻します。 time.sleep
は、ブロッキング関数の例です。プログラムの実行フローはちょうどそこで停止し、 time.sleep
終了後にtime.sleep
ます。
実際のライブの例は、ブロッキング関数のみで(当面は)構成されているrequests
ライブラリです。 asyncio
内でその関数のいずれかを呼び出すと、並行処理は行われません。 aiohttp
一方ではで構築されたasyncio
心の中で。コルーチンが同時に実行されます。
並行して実行したい、長時間実行しているCPUバウンドタスクがある場合は、
asyncio
はあなたのためではありません。そのためには、threads
またはmultiprocessing
が必要です。IOバウンドジョブを実行している場合は 、
asyncio
を使用して同時に実行できます。