Python - 非同期関数が完了するのを待ちます
この記事では、非同期関数を作成し、await
キーワードを使用してプロセスを中断する方法を示します。 また、Python でスレッドの代わりにタスクを使用する方法も学びます。
await
キーワードを使用して非同期関数を作成する
非同期プログラミングはマルチスレッドではありません。 マルチプロセッシングではなく、並行プログラミングです。
並行プログラミングの全体的な考え方やコーディング パターン全体については説明しませんが、基本原則とそれらを Python で実装する方法について説明します。
それでは、簡単な例を見てみましょう。 Func_1
、Func_2
、Func_3
が呼び出されています。
Func_1()
Func_2()
Func_3()
これらの関数が非同期で呼び出される場合、これは Func_1()
を呼び出してから Func_2()
を呼び出すことを意味します。
Func_1()
がそう返すとき、Func_2()
だけを呼び出し、Func_2()
が返るとき、Func_3()
を呼び出します。
マルチスレッドまたはマルチプロセッシングを使用する場合、それは非同期プログラミングと同じではありません。 マルチスレッドでは、この場合 3つのスレッドを定義し、これらすべての関数を同時に実行するためです。
または、ほぼ同時に、それらを同時に実行しようとするか、少なくとも同時実行の錯覚を作成します。
しかし、たとえば、Func_1()
が何か生産的なことを行ってから、データベースや API からデータを要求したり、単に待機するために一般的にスリープしたりしたいとしましょう。
その場合、CPU 時間を浪費して、この関数がまだ戻っていなくても Func_2()
の実行を開始したくありません。 したがって、同時に実行できるタスクは 1つだけです。 マルチプロセッシングやマルチスレッドは行っていません。
しかし、Func_1()
がスリープ状態、待機中、または非生産的である場合は、その時間を利用して Func_2()
およびおそらく Func_3()
の実行を開始できます。 Python で非同期プログラミングを行うには、asyncio
というライブラリをインポートする必要があります。
プログラム全体を非同期として定義しないため、特定の関数は非同期になります。 async
キーワードを使用して非同期関数を指定する必要があります。
この Main_Func()
だけだとプログラム全体が非同期になりますが、次の例では他の関数を追加します。 この関数内で、2つの print()
関数を使用します。
そして、その合間にスリープに入りますが、time.sleep()
でスリープするわけではありません。 asyncio.sleep()
を使用します。
asyncio.sleep()
を呼び出す前に await
キーワードを使用する必要があります。これは、2 番目の print ステートメントが終了するまで待機することを意味します。 終了する前に、他に何もするつもりはありません。
Main_Func()
関数を実行するには、asyncio.run()
を使用する必要があり、run()
関数内で Main_Func()
関数を渡します。 Main_Func()
関数を呼び出す必要があります。 マルチスレッドと呼んでいるだけではありません。
import asyncio
async def Main_Func():
print("Before waiting")
await asyncio.sleep(1)
print("After waiting")
asyncio.run(Main_Func())
出力:
Func_2()
という別の非同期関数を紹介しましょう。 2つのステートメントを出力し、2 秒間スリープします。
Main_Func()
関数内では、スリープする代わりに Func_2()
関数を await
キーワードで呼び出しますが、これは非同期ではありません。
import asyncio
async def Main_Func():
print("Before waiting")
await Func_2()
print("After waiting")
async def Func_2():
print("Func_2: Before waiting")
await asyncio.sleep(2)
print("Func_2: After waiting")
asyncio.run(Main_Func())
関数を待機しているため、非同期で呼び出しているのと同じであるため、すべての命令が定義どおりに完了するまで、関数は実行されません。
Before waiting
Func_2: Before waiting
Func_2: After waiting
After waiting
非同期ではありません。 ただし、Main_Func()
関数が呼び出されたときにそのようなことをしたい場合、コントロールは Main_Func()
関数の最初の print
ステートメントを出力する必要があります。
次に、Func_2()
関数を呼び出して、この関数の最初の print
ステートメントを出力します。
この関数がスリープしている間、Main_Func()
関数の 2 番目の print
ステートメントを出力する必要があります。これが完了すると、Func_2()
関数の 2 番目の print
ステートメントを出力する必要があります。
create_task()
を使用してタスクを作成し、問題を修正する
そのためには、tasks
を使用する必要があるため、Main_Func()
関数の先頭で、asyncio.create_task()
を呼び出しながらタスクを作成し、タスク内で、 Func_2()
を渡します。
これは、アイドル時間ができたら、そのタスクを呼び出すことを意味します。
import asyncio
async def Main_Func():
Task = asyncio.create_task(Func_2())
print("Before waiting")
print("After waiting")
async def Func_2():
print("Func_2: Before waiting")
await asyncio.sleep(2)
print("Func_2: After waiting")
asyncio.run(Main_Func())
コードを実行すると、Main_Func()
関数から 2つの print ステートメントが出力され、完了したことがわかります。 最初の print
ステートメントが実行されますが、2 番目の print
ステートメントを出力する前に実行が終了します。
Before waiting
After waiting
Func_2: Before waiting
Main_Func()
がメイン関数であるため、コントロールは Func_2()
関数を待機していません。つまり、コントロールが Main_Func()
関数の最後に到達すると、コントロールは実行を停止します。 .
コントロールは、Func_2()
関数の 2 番目の print
ステートメントが終了するのを待つ必要がないため、コントロールはそれをスキップします。 これを修正するには、Main_Func()
関数の最後で await Task
を使用します。
import asyncio
async def Main_Func():
Task = asyncio.create_task(Func_2())
print("Before waiting")
print("After waiting")
await Task
async def Func_2():
print("Func_2: Before waiting")
await asyncio.sleep(2)
print("Func_2: After waiting")
asyncio.run(Main_Func())
これで、定義されたとおりに印刷されていることがわかります。
Before waiting
After waiting
Func_2: Before waiting
Func_2: After waiting
これが非同期でどのように機能するかを確認したい場合は、print
ステートメントの間に sleep()
関数を使用して実行できます。
これは、Main_Func()
の最初の print
ステートメントを実行し、次にコントロールが 1 秒間スリープすることを意味します。これは、メイン関数がアイドル時間になったことを意味します。
これで、Main_Func()
が完全に実行されるまで、タスクを実行する時間があります。 Main_Func()
関数がスリープ状態であるため、タスクを使用して呼び出して Func_2()
関数の実行を開始するための CPU 時間が利用できるようになりました。
ただし、Func_2()
関数もスリープ状態になります。これは、この関数内でコントロールが 2 秒間待機し、コントロールが Main_Func()
関数に移動して 2 番目の print
ステートメントを出力することを意味します。
次に終了します。これは、コントロールが Func_2()
関数の残りの部分に関心を持たなくなったことを意味します。
async def Main_Func():
Task = asyncio.create_task(Func_2())
print("Before waiting")
await asyncio.sleep(1)
print("After waiting")
出力:
Before waiting
Func_2: Before waiting
After waiting
それに興味がある場合は、Main_Func()
関数の最後で await Task
を内部で使用する必要があります。
これは非同期です。 ご覧のとおり、順番は Main_Func()
関数が最初の print
ステートメントを出力し、次に Func_2()
が最初に出力し、次に Main_Func()
が 2 番目と Func_2()
を出力します。 秒を印刷します。
関数がスリープ状態になると、CPU 時間が無駄にならないようにアイドル時間が使用されるためです。
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
LinkedIn