I.はじめに
コルーチンは軽量スレッドであり、ユーザーレベル スレッドまたはグリーン スレッドとも呼ばれます。これは、カーネル モードのスレッド スイッチングよりも軽量で、多数の同時操作を効率的にサポートできるユーザー モードのコンテキスト スイッチング方式です。
2. コルーチンを使用する利点
Python のコルーチンは、asyncio モジュールを通じて実装されます。コルーチンを使用すると、次の利点が得られます。
- 軽量の同時操作: コルーチンの実行中にスレッドの切り替えやコンテキストの切り替えが必要ないため、これらの操作のオーバーヘッドが回避されるため、従来のスレッドよりも軽量で、より多くの同時操作をサポートできます。
- プログラムのパフォーマンスを向上させる: コルーチンはスレッド コンテキストの切り替えのオーバーヘッドを回避し、プログラムの実行効率を大幅に向上させることができます。また、Python ではコルーチンのスケジューリングは
イベント ループ (Event Loop) で行われますが、このイベント ループはシングルスレッド方式を採用しているため、CPU リソースを最大限に活用し、プログラムのパフォーマンスをさらに向上させることができます。 - 非同期プログラミングの簡素化: コルーチンにより非同期プログラミングが容易になり、非同期プログラム作成時のコールバック ヘル (コールバック ヘル) の問題が回避され
、プログラムの可読性と保守性が向上します。また、Python の async/await
構文を使用すると、非同期コードを同期的な方法で作成できるため、コードの理解とデバッグが容易になります。 - より柔軟な制御プロセス: コルーチンは一時停止および再開操作を簡単に実行できるため、プロセスを非常に柔軟に制御できます。たとえば、条件判断、ループ、その他のステートメントをコルーチンで使用して、より複雑なプロセス制御操作を実行できます。
つまり、コルーチンは効率的で軽量な同時プログラミング手法であり、プログラムのパフォーマンスを向上させ、非同期プログラミングを簡素化し、制御プロセスをより柔軟にすることができ、Python の重要な同時プログラミング ツールの 1 つです。
3. コード例
1. 基本的な使い方
import asyncio
async def task1():
await asyncio.sleep(1)
print('Task 1 done')
async def task2():
await asyncio.sleep(2)
print('Task 2 done')
async def main():
print('Starting tasks')
# 并发执行 task1 和 task2 任务
await asyncio.gather(task1(), task2())
'''列表传参启动'''
# tasks = [task1(), task2()] # 创建协程任务列表
# await asyncio.gather(*tasks) # 同时运行所有协程任务
print('All tasks done')
asyncio.run(main())
リスト パラメーター メソッドを使用してタスクを渡して開始することもできます。
tasks = [task1(), task2()] # 创建协程任务列表
await asyncio.gather(*tasks) # 同时运行所有协程任务
演算結果
2. 高度な使用法: 非同期タスクループタスク
方法 1:
import asyncio
import random
async def producer(queue):
while True:
value = random.randint(0, 10)
print(f"Produced: {
value}")
await queue.put(value)
await asyncio.sleep(random.random())
async def consumer(queue):
while True:
value = await queue.get()
print(f"Consumed: {
value}")
await asyncio.sleep(random.random())
async def main():
queue = asyncio.Queue()
task_producer = asyncio.create_task(producer(queue))
task_consumer = asyncio.create_task(consumer(queue))
await asyncio.gather(task_producer, task_consumer)
asyncio.run(main())
演算結果
上記のコードでは、プロデューサー関数と関数を定義しますconsumer
。どちらもasyncio.Queue
オブジェクトを入力として受け入れ、その中にコルーチンのロジックを実装します。このproducer
関数はキュー内で乱数を継続的に生成してキューに入れ、consumer
継続的にキューから乱数を取り出して消費します。
main 関数では、まずasyncio.Queue
コルーチン間の通信用のオブジェクトを作成し、次にasyncio.create_task
その関数を使用して 2 つのタスク (プロデューサー タスクとコンシューマー タスク) を作成します。最後に、関数を使用してasyncio.gather
両方のタスクを実行し、どちらかのタスクが完了すると main 関数も終了します。
コルーチンを使用する場合、await
キーワードを使用してコルーチンの実行を一時停止したり、他のコルーチンの実行を待機したり、I/O 操作の完了を待機したりする必要があることに注意してください。同時に、コルーチンではブロッキング操作を使用しないでください。ただし、time.sleep()
などの非同期 I/O 操作を使用する必要がありますasyncio.sleep()
。
方法 2 と方法 3
import asyncio
import random
async def producer(queue):
while True:
value = random.randint(0, 10)
print(f"Produced: {
value}")
await queue.put(value)
await asyncio.sleep(random.random())
async def consumer(queue):
while True:
value = await queue.get()
print(f"Consumed: {
value}")
await asyncio.sleep(random.random())
async def main():
queue = asyncio.Queue()
'''方法一'''
# task_producer = asyncio.create_task(producer(queue))
# task_consumer = asyncio.create_task(consumer(queue))
# await asyncio.gather(task_producer, task_consumer)
'''方法二:直接启动'''
# await asyncio.wait([producer(queue), consumer(queue)])
'''方法三'''
task_producer = asyncio.ensure_future(producer(queue))
task_consumer = asyncio.ensure_future(consumer(queue))
await asyncio.wait([task_producer(queue), task_consumer(queue)])
asyncio.run(main())
上記のコルーチン起動メソッドの違いは、タスクの完了を待つ方法にあります。
await asyncio.gather(task_producer, task_consumer)
複数のタスクを同時に実行し、すべてのタスクが完了するまで待ってから結果を返します。つまり、このメソッドはすべてのタスクが完了した後にのみ次のステップに進みます。await asyncio.wait([producer(queue), consumer(queue)])
次に、複数のコルーチン オブジェクト (リストを介して渡される) が関数に渡されasyncio.wait()
、この関数も複数のコルーチンが同時に完了するのを待ちます。違いは、asyncio.wait()
関数が 2 つのコレクション (done と pending) を返し、done コレクションには完了したタスクが含まれ、pending コレクションにはまだ完了していないタスクが含まれていることです。したがって、コルーチンの結果は、done コレクションを走査することで取得できます。asyncio.ensure_future()
コルーチン関数は Future オブジェクトとしてラップでき、非同期実行のためにイベント ループに追加できます。コルーチン関数を直接呼び出す場合とは異なり、asyncio.ensure_future() を使用すると、Future オブジェクトが作成されて返され、管理や操作のために他の場所に渡すことができます。- これを使用する主な利点
asyncio.ensure_future()
は、複数のコルーチン間でオブジェクトを共有できるため、コルーチンが連携して動作できることです。同時に、コルーチン関数が読みやすくなり、コルーチン間でデータを受け渡すときに便利になります。 - 一方、コルーチン関数を直接呼び出し、 await キーワードを使用してその完了を待つことは完全に実現可能です。場合によっては、このアプローチの方がよりシンプルで直感的になることもあります。ただし、複数のコルーチンを同時に実行する必要がある場合、または asyncio ライブラリで提供される他の高度な機能を使用する場合は、 asyncio.ensure_future() を使用する方が便利な場合があります。
一般的に
asyncio.gather()
より簡単で使いやすく、複数のタスクを同時に実行し、すべてのタスクが完了するのを待つのに適しています。asyncio.wait()
より柔軟で、タスクの実行結果をリアルタイムに取得できます。asyncio.ensure_future()
これは、コルーチン プログラミングのほとんどの場合に機能するより一般的なアプローチですが、場合によっては、コルーチン関数への直接呼び出しを使用して待機する方が簡単で効率的である場合もあります。
3. 起動方法の違い
asyncio.get_event_loop().run_until_complete()
Python 3.7 より前では、コルーチン タスクを実行するには関数を使用する必要があることに注意してください。ただし、Python 3.7
それ以降のバージョンでは、より簡潔なasyncio.run()
関数を使用してコルーチン タスクを実行できるようになりました。
サンプルコードは次のとおりです。
import asyncio
import random
async def producer(queue):
while True:
value = random.randint(0, 10)
print(f"Produced: {
value}")
await queue.put(value)
await asyncio.sleep(random.random())
async def consumer(queue):
while True:
value = await queue.get()
print(f"Consumed: {
value}")
await asyncio.sleep(random.random())
async def main():
queue = asyncio.Queue()
'''方法一'''
# task_producer = asyncio.create_task(producer(queue))
# task_consumer = asyncio.create_task(consumer(queue))
# await asyncio.gather(task_producer, task_consumer)
'''方法二:直接启动'''
# await asyncio.wait([producer(queue), consumer(queue)])
'''方法三'''
task_producer = asyncio.ensure_future(producer(queue))
task_consumer = asyncio.ensure_future(consumer(queue))
await asyncio.wait([task_producer(queue), task_consumer(queue)])
'''协程启动的方式一'''
# asyncio.run(main())
'''协程启动的方式二'''
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
new_loop.run_until_complete(main())
つまり、コルーチンは効率的で軽量な同時プログラミング手法であり、プログラムのパフォーマンスを向上させ、非同期プログラミングを簡素化し、制御プロセスをより柔軟にすることができ、Python の重要な同時プログラミング ツールの 1 つです。
以上は Python の基本的な使い方であるコルーチンについての紹介でしたので、お役に立てれば幸いです。