はじめに
筆者は去年4月にIT系の企業に入社したばかりの、新米ITエンジニアだ。
情報系のバックグラウンドもなく、大学院にいた頃の研究分野は全く関係ない分野で、プログラミングも趣味や研究で軽く触れた程度だ(よく入社できたな)。
何が言いたいかというと、この記事はプログラミング初心者が書いた記事なので正確性等は保証できないという話だ。
記事執筆の経緯
業務や社内の勉強会など色んな場面で「非同期処理」という言葉を聞くようになった。研修でもやった気がするが、その場では理解できず、復習する時間も取れなくてそのまま放置になっていた。
流石に理解しとかないとマズそうだなと思い、本記事の執筆に至る
chatGPTに聞いてみた
というわけで本題。「非同期処理」を理解しよう!
ただ、今までも非同期処理そのものをググったりchatGPTに聞いたりしたことはあったのだが、いまいちパッと理解ができなかった記憶がある。
そこで、「プログラマたるもの手を動かしてなんぼっしょ」ということでchatGPTに非同期処理の振る舞いを実感できるコードを書かせて、それを実際に動かしてみた。
投げたプロンプト
非同期処理について理解したい。
簡単なコードで、非同期処理の振る舞いを実感できるような題材をくれ
chatGPTさんからのお返事
非同期処理を理解するための一般的な例は、Webからのデータのフェッチや時間がかかる処理のシミュレーションです。Pythonでの非同期処理を示すシンプルな例を紹介するよ。ここでは、asyncioモジュールを使用して非同期処理を行う。以下の例では、非同期に複数のタスクを実行し、それぞれが完了するのを待つ方法を示す。
このコードでは、count関数が非同期に実行される。asyncio.sleep(1)は非ブロッキング待機を示し、この間に他のタスクが実行される余地を提供する。main関数ではasyncio.gatherを使って、3つのcountタスクを並行して実行する。結果として、プログラムの全体的な実行時間は大幅に短縮される。
この例から、非同期処理が複数のタスクを効率的に並行して処理できることを実感できるはずだ。非同期処理は、待機時間が長いタスク(例えば、ネットワークリクエストやデータベースへのクエリ)に特に有効である。
お出しされたコード
import asyncio
async def count():
print("One")
await asyncio.sleep(1) # 非同期に1秒待機
print("Two")
async def main():
await asyncio.gather(
count(),
count(),
count()
)
if __name__ == "__main__":
import time
start = time.time()
asyncio.run(main())
end = time.time()
print(f"Finished in {end - start} seconds.")
実行結果
$ python async.py
One
One
One
Two
Two
Two
Finished in 1.0014240741729736 seconds.
解説・雑感
いかがだっただろうか。
見てわかる通り、count
という1秒かかる(かける)処理を用意してあげて、それを3つ同時に実行したらどうなるか?ということをしている。
要するに、「1秒かかる処理が3つあっても並行してやれば1秒で終わるよね!」という話だと思われる。
もっと話を分かりやすくするために、非同期「じゃない」処理 (=同期処理と言って良いのか?)をchatGPTに実装してもらった。
chatGPTに実装してもらった非同期「じゃない」処理
import time
def count():
print("One")
time.sleep(1) # 同期的に1秒待機
print("Two")
def main():
count()
count()
count()
if __name__ == "__main__":
start = time.time()
main()
end = time.time()
print(f"Finished in {end - start} seconds.")
実行結果
$ python non_async.py
One
Two
One
Two
One
Two
Finished in 3.015064001083374 seconds.
解説・雑感
こちらは3つの処理が直列しており、1つの処理が終わってから次の処理を実行するという流れになっている。
実行結果を見ればわかる通り、非同期処理の場合 Oneが3つ並んでからTwoが3つ並んでいる (= 1つ目の処理が終わる前に2つ目の処理が終わっている) のに対し、同期処理の場合 "One Two" が3つ並んでいる (= 1つ目の処理が終わるまで待ってから2つ目の処理が始まっている)
そして、かかる時間も同期処理の場合は約3秒なのに対し、同期処理の場合は1秒で済んでいる。
この「時短できる」という性質が、「非同期処理」なんてものが(おそらく色々なプログラミング言語で)わざわざ実装されている理由なのだろう。
なんとなく分かったけど
今回の記事の執筆を通して、非同期処理についての理解が深まった。執筆前は非同期処理について聞かれてもほぼ説明できないレベルだったが、今は最低限の説明はできそうだ。
ただこれでは不十分。
まず今自分の頭の中では「並列処理みたいなもの」という大雑把な理解に留まっているが、わざわざ「非同期処理」という言葉を使っているあたりイコールで結びつけられるものではないのだろう。今後ちゃんとした定義について勉強する必要がありそう。
そして何より大事なのが、「非同期処理を適切に実装できること」。非同期処理によってプログラム全体の実行時間を短縮できる場面で、非同期処理という機能のことを思い出してちゃんと実装できる必要がある。
上記の2点は、今後の宿題といった感じだろうか。
終わりに
見て分かる通り、勉強中の内容をあえて途中でアウトプットしている。なので内容も理解も不十分だったり不正確だったりするかもしれないことは、改めて強調しておく。(誤っていたらご指摘くれると嬉しいです……!)
改めて、(ほぼchatGPT頼りとはいえ)実際にコードを動かして「具体例」を見ると、プログラミングにおける概念の理解は深まるものだと実感。そして、chatGPTはそういったコードの具体例の出力に長けているということにも気づいた。
今後も、chatGPTを活用したりしてプログラミングの様々な知識や概念を学んでいきたいと思う。
そんなプログラミング初心者の決意表明じみたぼやきなのであった。完。