2024.10.10
将来は卵1パックの価格が2倍に? 多くの日本人が知らない世界の新潮流、「動物福祉」とは
リンクをコピー
記事をブックマーク
福田隼也氏:ここまで並行には処理をしていないので、上のループの中で、もう少しawaitの動きを見ていきたいと思います。
考え中のお客さま3人から注文を受けてみましょう。先ほどのコルーチンです。上のところで注文を受けて、下のところでオーダーを受け付けるというところ。それぞれのコルーチンにawaitを付けて、menuを受け取ってみたいと思います。3人は考える時間が異なるので、引数を受け取るように変えて実行しています。
並行実行できていれば、3秒で終わります。結果、6秒かかっています。順番に実行されているようです。さらに動きを確認するために、orderのmenuの出力をそれぞれ受け取った直後にprintしてみましょう。
すると、逐次実行されていることがわかります。1つ目の注文を聞いて、聞き終わってから2つ目の注文を受けているのがわかると思います。コルーチンをawaitするだけでは、並行にはなりません。
ここで登場するのがタスクです。「Taskオブジェクト」というものがあります。コルーチンをラップして実行状態をもちます。イベントループにコルーチンを登録します。
作る方法は大きく2つです。Python3.7で追加された「asyncio.create_task」。あとは並列実行を助ける「gather」があるので覚えておいてください。
「初めてのタスク」です。orderの中で、create_taskでコルーチンのthinking_orderを渡してタスクを受け取ります。自分にawait taskをして、menuをもつコルーチンを受け取る流れになっています。
では、実行してみましょう。処理は3秒で終わっています。先ほどのように、awaitの間にprintを入れてみましょう。コルーチンがいつのタイミングで実行されているのかを確認したいと思います。
menu = await task、という真ん中の下にprintを入れていて、この下も、awaitした後にループで、awaitした後にループで、となっています。どうでしょう。最初のタスク。ここですね。ここが実行された時点で、登録されているすべてのタスクが実行されていることがわかるかと思います。
awaitされた時に中断、再開するのは、すでにイベントループに登録済みのタスクであることがわかるかと思います。
また、結果をまとめて受け取るgatherというものがあります。これは引数にコルーチン、またはタスクを受け取って、すべてが終わり次第、結果を投入した順で返します。
「tasks」というところに登録してあるコルーチンをまとめて実行して、結果を渡した順で返って来るというものになっています。
考え中のお客さまは、3人とも検索しないと決められないんです。なので検索をしてみましょう。なぜ検索をするかというと、asyncioは外部からの入力が欲しいからです。まずは、httpクライアントのrequestsを利用してみましょう。
「long_think」で、responseで受け取る値に「Google」で「おいしいお蕎麦は?」というのを投げています。これを実行してみましょう。
3秒かかりました。どうなんでしょう。この結果だとそれっぽく動いています。この中には実はブロッキングな処理が紛れ込んでいます。asyncio.runにdebugというものがあるんです。もう一度実行してみましょう。すると、時間のかかっている処理を検出してくれます。
メッセージは省略していますが「requests」を利用しているところで、0.915となっています。long_thinkで、requestsを行っているルーチンに何かあるよということがわかります。
asyncioはイベントループを利用して非同期IOを実現するため、IOの部分のモジュールもasyncioに対応している必要があります。requestsはasyncio対応していないため、イベントループである彼女の手のひらの上に乗っかっていない状態です。そのため、asyncio対応のhttpクライアントで実行を試したいと思います。
ここでは、asyncio対応のクライアントの「httpx」を利用してみましょう。実行してみると、処理の終了までに1秒かからずに完了していることがわかるかと思います。
このように、asyncioを利用するモジュールには、モジュール自体がasyncioに対応している必要があることがわかりました。
さて、「asyncioは怖くない」。コルーチンは関数にasyncを付けるだけで、肝はawaitです。タスク、切り替わる先のコルーチン。イベントループに登録しておくと、よきに計らって切り替えてくれます。イベントループは、登録したタスクをawaitのタイミングで切り替えます。ただし、切り替える先のものなので、asyncio対応している必要があります。
それぞれで必要な機能です。コルーチンにはasync/await。タスクにはcreate_taskで、コルーチンを受け取れます。イベントループに登録をします。そしてgatherです。引数にコルーチンをまとめて指定して、まとめて実行結果を受け取れます。イベントループでは、asyncio.runを紹介しました。get_running_loopやget_event_loopがありますが、余力があればまずはこれを抑えましょう。
ここまでお話ししていた中で、いくつかの武器を手に入れました。まずは並行処理。並行処理は処理を少しずつやります。そして非同期IO。外部からの入力を同期しないという概念でした。そしてこれらを実現しているのがasyncioです。コルーチンのタスクとして、イベントループに登録をして処理を並行に実行します。
タイミングとしてキーになるのがawaitです。これは外部からの入力、非同期IO、例ではhttp検索のタイミングをタスクとイベントループによって切り替えます。ちょっとasyncioはわがままで、イベントループで管理する外部と連携している部分は、asyncioに対応している必要があることがわかりました。
ここまでがasyncioです。お待たせしましたDjangoです。やっとここまできました。本日のお品書きはこんな感じです。
タイトルはこんな感じです。Django Async Viewの使い所。ASGI対応とasync view、asyncioのよきところを享受できるようになります。IOの発生する処理で、効果を発揮します。
例えばAPIのリクエストやIoTデバイスとのMQTT。Djangoでそれらを利用したい場合になります。
先ほどのお蕎麦の注文例ですね。この中で、「asyncio.run()はしないの?」と思うかもしれませんが、実は知らず知らずのうちにしています。
ASGI Djangoはどうやって実行しますか? UvicornやHypercornなどのASGI製のWebサーバーを利用しているかと思います。ASGIでPythonのWebサーバーとPythonのWebフレームなどをつなぐのに、ASGIサーバー側でイベントループの実行しています。
非同期な処理を行いたい場合には、ASGIアプリケーションとして実行することがあります。ただ、非同期な処理の中にも、同期の処理、asyncio対応していないものがあるかもしれません。
その場合、同期処理にはDjangoが用意している同期処理を非同期処理にする「sync_to_async」を利用しましょう。
sync_to_asyncは、同期関数を受け取って、それをラップして非同期関数を返します。直接、ラッパーまたはデコレータとして利用できます。
asyncio対応していない同期処理をイベントループに登録できます。Django側は、sync_to_asyncを用意していますが、これはDjangoに限った話ではありません。ほかにも「concurrent.futures」という、threadingとmultiprocessingを容易に使える標準ライブラリがあるので、これらを利用してイベントループに登録できます。
ThreadPoolExecutorとtProcessPoolExecutorを利用して、同期処理をブロッキングすることなくイベントループを利用できるようになります。非同期にはできますが、実行にはコストがかかり、速度に影響がある場合もあります。
ASGI対応とasync viewです。これによって、Djangoでもasyncioと非同期IO/並行処理の恩恵にあずかることができるようになりました。また、同期処理についても、concurrent.futuresを使ったラップが可能です。ただ、まだ残っている同期処理があるんです。最後の砦のDjangoのORMが残っています。
Node.jsの作者であるRyan Dahl氏は「データベースにクエリを実行している間、ソフトウェアは何をしているのですか?」と尋ねました。もちろん答えは何もありませんでした。データベースが応答するのを待っていました。
Djangoを利用する上で、大きな魅力の1つであるORMはasyncio対応していません。非同期IOの利点を外部の入出力として説明してきました。主に今回の話の中では、ネットワークでした。
もちろんデータベースへのアクセスも有効です。そこができると全体が速くなります。今触っているアプリケーションを思い出してください。Webでデータベースが非同期になったら、早くなることが想像ができるはずです。
DjangoコアデベロッパーのAndrew Godwinさんは、去年の「PyConline AU 2020」で 「Taking Django's ORM Async」というお話をしていました。
その中で、「APIのデザインが重要だ。ただasync DBAPIというPEPはまだない、課題はある」とお話をしていました。その話の中で、安全な非同期プログラミング、すべてが非同期でエラーを起こせない世界を実現する。非同期を可能な限り最高なものにするとおっしゃっていたのが印象的でした。
具体的なバージョンについてです。Djangoのフォーラムの中での最近のやりとりです。具体的なバージョンは、もう少しかかりそうです。「my initial goal was 4.0, but...」というふうに続きます。
asyncioが多少怖くなくなったと思っていただけるとうれしいです。Djangoのasync view、同期と非同期を併せて、IOバウンドな処理を中心に利用してください。Async ORM、楽しみですね。Pythonでは、ほかにもasyncio対応のORMやアダプターがすでにあります。そちらと併せて利用するのも良いかもしれません。
また、DjangoはOSSです。リポジトリをのぞいてみるのもいいかもしれません。以上です。ご清聴ありがとうございました。
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略