Python - 非同期関数が完了するのを待ちます

Salman Mehmood 2023年6月21日
  1. await キーワードを使用して非同期関数を作成する
  2. create_task() を使用してタスクを作成し、問題を修正する
Python - 非同期関数が完了するのを待ちます

この記事では、非同期関数を作成し、await キーワードを使用してプロセスを中断する方法を示します。 また、Python でスレッドの代わりにタスクを使用する方法も学びます。

await キーワードを使用して非同期関数を作成する

非同期プログラミングはマルチスレッドではありません。 マルチプロセッシングではなく、並行プログラミングです。

並行プログラミングの全体的な考え方やコーディング パターン全体については説明しませんが、基本原則とそれらを Python で実装する方法について説明します。

それでは、簡単な例を見てみましょう。 Func_1Func_2Func_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())

出力:

python 非同期関数が完了するのを待つ - 1 を出力する

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 時間が無駄にならないようにアイドル時間が使用されるためです。

python 非同期関数が完了するのを待つ - 出力 2

著者: Salman Mehmood
Salman Mehmood avatar Salman Mehmood avatar

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

関連記事 - Python Async