asyncio
を使用するようにスレッド化されたプログラムを移植する方法を理解しようとしています。いくつかの標準ライブラリ Queues
を中心に同期するコードがたくさんあります。基本的には次のようになります。
import queue, random, threading, time
q = queue.Queue()
def produce():
while True:
time.sleep(0.5 + random.random()) # sleep for .5 - 1.5 seconds
q.put(random.random())
def consume():
while True:
value = q.get(block=True)
print("Consumed", value)
threading.Thread(target=produce).start()
threading.Thread(target=consume).start()
1つのスレッドが値(ユーザー入力の可能性があります)を作成し、別のスレッドが値を使用して何かを行います。重要なのは、これらのスレッドは新しいデータが存在するまでアイドル状態であり、その時点でスレッドが起動して何かを行うことです。
このパターンをasyncioを使用して実装しようとしていますが、「実行」する方法がわかりません。
私の試みは多かれ少なかれこのように見えます(そして何もしません)。
import asyncio, random
q = asyncio.Queue()
@asyncio.coroutine
def produce():
while True:
q.put(random.random())
yield from asyncio.sleep(0.5 + random.random())
@asyncio.coroutine
def consume():
while True:
value = yield from q.get()
print("Consumed", value)
# do something here to start the coroutines. asyncio.Task()?
loop = asyncio.get_event_loop()
loop.run_forever()
コルーチンの使用ではなく、タスクでのラッピング、フューチャーを作成または返すようにするなどのバリエーションを試しました。
私はasyncioをどのように使用するべきかについて間違った考えを持っていると思い始めています(おそらく、このパターンは私が気付いていない別の方法で実装する必要があります)。どんなポインタでもいただければ幸いです。
はい、正確に。タスクはあなたの友達です:
_import asyncio, random
q = asyncio.Queue()
@asyncio.coroutine
def produce():
while True:
yield from q.put(random.random())
yield from asyncio.sleep(0.5 + random.random())
@asyncio.coroutine
def consume():
while True:
value = yield from q.get()
print("Consumed", value)
loop = asyncio.get_event_loop()
loop.create_task(produce())
loop.create_task(consume())
loop.run_forever()
_
_asyncio.ensure_future
_は、タスクの作成にも使用できます。
また、覚えておいてください:q.put()
はコルーチンなので、yield from q.put(value)
を使用する必要があります。
[〜#〜]更新[〜#〜]
例では、asyncio.Task()
/asyncio.async()
から新しいブランドのAPI loop.create_task()
およびasyncio.ensure_future()
に切り替えられました。
これが私が本番で使用しているもので、Gistに移動しました: https://Gist.github.com/thehesiod/7081ab165b9a0d4de2e07d321cc2391d
少し後で、たぶんOTは、独立したコンシューマーであったため、複数のタスクからQueue
から消費できることを覚えておいてください。
次のスニペットは、asyncio
タスクで同じスレッドプールパターンを実現する方法を例として示しています。
q = asyncio.Queue()
async def sum(x):
await asyncio.sleep(0.1) # simulates asynchronously
return x
async def consumer(i):
print("Consumer {} started".format(i))
while True:
f, x = await q.get()
print("Consumer {} procesing {}".format(i, x))
r = await sum(x)
f.set_result(r)
async def producer():
consumers = [asyncio.ensure_future(consumer(i)) for i in range(5)]
loop = asyncio.get_event_loop()
tasks = [(asyncio.Future(), x) for x in range(10)]
for task in tasks:
await q.put(task)
# wait until all futures are completed
results = await asyncio.gather(*[f for f, _ in tasks])
assert results == [r for _, r in tasks]
# destroy tasks
for c in consumers:
c.cancel()
asyncio.get_event_loop().run_until_complete(producer())