コルーチンが将来プリエンプションされる可能性があるためですか?または、クリティカルセクション(IMOは推奨されるべきではありません)からの利回りを使用できるようにしますか?
スレッドコードでロックを使用するのと同じ理由で、クリティカルセクションを保護するために使用します。 asyncio
は主にシングルスレッドコードでの使用を目的としていますが、同時実行が発生しているため(yield from
またはawait
を押すたびに)、同期が必要になる場合があります。
たとえば、Webサーバーからデータをフェッチし、結果をキャッシュする関数について考えてみます。
async def get_stuff(url):
if url in cache:
return cache[url]
stuff = await aiohttp.request('GET', url)
cache[url] = stuff
return stuff
ここで、get_stuff
の戻り値を使用する必要がある可能性のある複数のコルーチンが同時に実行されていると仮定します。
async def parse_stuff():
stuff = await get_stuff()
# do some parsing
async def use_stuff():
stuff = await get_stuff()
# use stuff to do something interesting
async def do_work():
out = await aiohttp.request("www.awebsite.com")
# do some work with out
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
parse_stuff(),
use_stuff(),
do_work(),
))
ここで、url
からのデータのフェッチが遅いであると仮定します。 parse_stuff
とuse_stuff
の両方が同時に実行される場合、それぞれがstuff
をフェッチするためにネットワークを経由する全コストに見舞われます。メソッドをロックで保護する場合は、次のことを回避します。
stuff_lock = asyncio.Lock()
async def get_stuff(url):
async with stuff_lock:
if url in cache:
return cache[url]
stuff = await aiohttp.request('GET', url)
cache[url] = stuff
return stuff
もう1つの注意点は、1つのコルーチンがget_stuff
内にあり、aiohttp
呼び出しを行い、別のコルーチンがstuff_lock
を待機している間、get_stuff
を呼び出す必要がまったくない3番目のコルーチンも実行できます。 Lock
でのコルーチンブロッキングの影響を受けます。
明らかに、この例は少し工夫されていますが、うまくいけば、asyncio.Lock
が役立つ理由がわかります。これにより、クリティカルセクションへのアクセスを必要としない他のコルーチンの実行をブロックすることなく、クリティカルセクションを保護できます。