[Python] 19. 非同期IOのasync/await(asyncio)を使う方法


Study / Python    作成日付 : 2020/06/22 18:10:12   修正日付 : 2020/06/22 18:10:12

こんにちは。明月です。


この投稿はPythonで非同期IOのasync/await(asyncio)を使う方法に関する説明です。


Threadで関数を実行する時にその結果を受け取ることが難しいです。

link - [Python] 17. スレッド(Thread)とロック(lock)、そしてデッドロック(deadlock)

つまり様々な制御文を並列で処理して結果をすべて待たなければならないです。

# スレッドとスレッド停止するモジュール
import threading, time
# 結果の変数
ret = 0
def example():
  # グローバル変数を使用
  global ret
  # 1から9まで繰り返す。
  for i in range(1,10,1):
    # 0.1秒待つ
    time.sleep(0.1)
    # 変数を増加
    ret += i
# example関数をスレッドで開始する。
th1 = threading.Thread(target=example)
th2 = threading.Thread(target=example)
# スレッド開始
th1.start()
th2.start()
# スレッドが終了する時まで待つ。
th1.join()
th2.join()
# 結果出力
print(ret)


上みたいにfor文を二つのスレッドに実行して計算することができます。でも、何かソースが複雑みたいです。

グローバル変数でretを生成して各スレッドでデータを入力、修正します。もし各スレッドでパラメータによってデータを変わることにしようと思うとすごく複雑になります。


でも非同期処理(async/await(asyncio))を使うと簡単に処理することができます。

# async/awaitを使うためのモジュール
import asyncio

# 関数の前にasync宣言して非同期を指定した。
async def example():
  # 変数宣言
  ret = 0
  # 1から9まで繰り返す。
  for i in range(1,10,1):
    # 非同期処理 0.1を待機
    await asyncio.sleep(0.1)
    # 変数を増加
    ret += i
  # 結果を返却
  return ret

# 非同期を使うためにはasync関数で実行する。
async def main():
  # example関数を開始
  t1 = asyncio.create_task(example())
  t2 = asyncio.create_task(example())

  # t1とt2が終了すれば結果値を出してコンソールに出力
  print(await t1 + await t2)

#async defを実行するための関数。
asyncio.run(main())


asyncとawaitを使えばthreadingを使う時よりソースを綺麗に作成することができます。


まず、asyncとは関数の前に使うキーワードです。外部では非同期を実行するためのキーワードだし、内部的にはawaitを使えるような予約語です。

main関数でt1とt2を待つawaitを使いましたが、もしmainがasyncではないしグローバル領域でawaitキーワードを使うとエラーが発生することになります。

awaitキーワードは非同期で待機する意味で、await asyncio.sleep(i)の場合はiを秒単位です待機する意味です。つまりtime.sleepと同じ意味です。

create_task担っているtaskオブジェクトからawaitを使えばスレッドが終了する時まで待機するthread.joinと同じ意味です。


最後にasyncio.runはasyncをcreate_taskみたいに非同期ではなく同期(プロセスの順番とおりに処理)で処理する呼び出しです。

asyncio.runを使ってもよいです。でも最近はrun_until_completeを使うことをお勧めしています。

またcreate_taskで非同期に分けましたが、asyncio.gatherも非同期処理が可能です。

# async/awaitを使うためのライブラリ
import asyncio

# 非同期を指定された関数
async def example():
  # 変数設定
  ret = 0
  # 1から9まで繰り返す。
  for i in range(1,10,1):
    # 非同期処理 0.1を待機
    await asyncio.sleep(0.1)
    # 変数を増加
    ret += i
  # 結果を返却
  return ret

# 非同期を使うためにはasync関数で実行する。
async def main():
  # example関数を非同期で開始する。パラメータに関数を二つ入れることで非同期を二回に実行することと同じ意味。
  ret = await asyncio.gather(example(), example())
  # コンソール出力
  print(sum(ret))

#async defを実行するような関数。
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
  # 非同期を待つ。
  loop.run_until_complete(main())
finally:
  loop.close()
  asyncio.set_event_loop(None)


link - https://docs.python.org/3/library/asyncio-task.html

link - https://docs.python.org/3/library/asyncio-eventloop.html

link - https://stackoverflow.com/questions/55590343/asyncio-run-or-run-until-complete


ここまでPythonで非同期IOのasync/await(asyncio)を使う方法に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

最新投稿