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を使用して同時に実行できます。