実践的なPython開発者向けのAsyncIO

「どれだけゆっくりと動作するのか、呼び出しを並列化するとどうなるのか?」



それからasyncioに出会い、すべてが変わりました。



誰も知らない場合、asyncioは競合プログラミングの組織向けの新しいモジュールであり、Python 3.4で登場しました。 非同期コードでコルーチンと将来の使用を簡素化することを目的としています-コールバックなしでコードが同期のように見えるようにします。



当時、似たようなツールがいくつかあり、そのうちの1つが際立っていたことを思い出します。これがgeventライブラリです。 実践的なpython開発者向けの優れたgeventチュートリアルを読むことをお勧めします。このチュートリアルでは、作業方法だけでなく、一般に競合が理解されているものについても説明しています。 私はこの記事がとても気に入ったので、asyncioの概要を書くためのテンプレートとして使用することにしました。



小さな免責事項は、gevent対asyncioの記事ではありません。 ネイサンロードは彼のメモですでにこれをしていました。 GitHubですべての例を見つけることができます。



あなたはコードを書くのを待つことができないことを知っていますが、最初に将来的に私たちに役立ついくつかの概念を検討したいと思います。



スレッド、イベントループ、コルーチン、および先物



スレッドは最も一般的なツールです。 聞いたことがあると思いますが、asyncioでは、イベントサイクル、コルーチン、未来など、他のいくつかの概念を使用しています。





とても簡単ですか? 行こう!



同期および非同期実行



競争は並行性ではなく、より良い 」というビデオでは、ロブパイクが重要なことに注意を向けています。 タスクを競争力のあるサブタスクに分割することは、彼がこれらのサブタスクも管理している場合にのみ、このような並列処理でのみ可能です。



Asyncioも同じです-コードをコルーチンとして定義されたプロシージャに分割することができます。これにより、同時実行を含め、必要に応じてそれらを管理することができます。 コルーチンにはyieldステートメントが含まれており、これを使用して、完了するのを待っている他のタスクに切り替えることができる場所を決定します。



asyncioでコンテキストを切り替えるには、yieldが責任を負います。yieldは制御をイベントループに戻し、次にイベントループは別のコルーチンに進みます。 基本的な例を考えてみましょう:



import asyncio async def foo(): print('Running in foo') await asyncio.sleep(0) print('Explicit context switch to foo again') async def bar(): print('Explicit context to bar') await asyncio.sleep(0) print('Implicit context switch back to bar') ioloop = asyncio.get_event_loop() tasks = [ioloop.create_task(foo()), ioloop.create_task(bar())] wait_tasks = asyncio.wait(tasks) ioloop.run_until_complete(wait_tasks) ioloop.close()
      
      





 $ python3 1-sync-async-execution-asyncio-await.py Running in foo Explicit context to bar Explicit context switch to foo again Implicit context switch back to bar
      
      





*最初に、asyncioからのスリープを使用してノンブロッキングのふりをするいくつかの簡単なコルーチンを発表しました

*コルーチンは別のコルーチンからのみ起動するか、 create_taskを使用してタスクにラップできます。

* 2つのタスクが完了したら、 waitを使用してそれらを結合

*最後に、 run_until_completeを介してイベントループに実行を送信します



したがって、ある種のコルーチンでawaitを使用し 、コルーチンがイベントループに制御を戻すことができることを宣言します。これにより、次のタスクのいくつかが起動されます。 同じことがbarでも起こります: await asyncio.sleepコントロールはイベントループに戻され、適切なタイミングでfooに戻ります。



2つのブロックタスクを想像してください。gr1とgr2は、特定のサードパーティサービスにアクセスしているかのようであり、応答を待っている間、3番目の関数は非同期に動作できます。



 import time import asyncio start = time.time() def tic(): return 'at %1.1f seconds' % (time.time() - start) async def gr1(): # Busy waits for a second, but we don't want to stick around... print('gr1 started work: {}'.format(tic())) await asyncio.sleep(2) print('gr1 ended work: {}'.format(tic())) async def gr2(): # Busy waits for a second, but we don't want to stick around... print('gr2 started work: {}'.format(tic())) await asyncio.sleep(2) print('gr2 Ended work: {}'.format(tic())) async def gr3(): print("Let's do some stuff while the coroutines are blocked, {}".format(tic())) await asyncio.sleep(1) print("Done!") ioloop = asyncio.get_event_loop() tasks = [ ioloop.create_task(gr1()), ioloop.create_task(gr2()), ioloop.create_task(gr3()) ] ioloop.run_until_complete(asyncio.wait(tasks)) ioloop.close()
      
      





 $ python3 1b-cooperatively-scheduled-asyncio-await.py gr1 started work: at 0.0 seconds gr2 started work: at 0.0 seconds Lets do some stuff while the coroutines are blocked, at 0.0 seconds Done! gr1 ended work: at 2.0 seconds gr2 Ended work: at 2.0 seconds
      
      





I / Oとスケジューリングがどのように機能するかに注意を払い、これらすべてを単一のスレッドに収めることができます。 I / O待機によって2つのタスクがブロックされている限り、3番目の機能はすべてのプロセッサ時間を占有できます。



実行順序



同期の世界では、順番に考えます。 完了するまでに時間がかかるタスクのリストがある場合、それらは処理されたのと同じ順序で終了します。 しかし、競争の場合、これを確認することはできません。



 import random from time import sleep import asyncio def task(pid): """Synchronous non-deterministic task. """ sleep(random.randint(0, 2) * 0.001) print('Task %s done' % pid) async def task_coro(pid): """Coroutine non-deterministic task """ await asyncio.sleep(random.randint(0, 2) * 0.001) print('Task %s done' % pid) def synchronous(): for i in range(1, 10): task(i) async def asynchronous(): tasks = [asyncio.ensure_future(task_coro(i)) for i in range(1, 10)] await asyncio.wait(tasks) print('Synchronous:') synchronous() ioloop = asyncio.get_event_loop() print('Asynchronous:') ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 1c-determinism-sync-async-asyncio-await.py Synchronous: Task 1 done Task 2 done Task 3 done Task 4 done Task 5 done Task 6 done Task 7 done Task 8 done Task 9 done Asynchronous: Task 2 done Task 5 done Task 6 done Task 8 done Task 9 done Task 1 done Task 4 done Task 3 done Task 7 done
      
      





もちろん、各タスクはランダムにスリープ状態になるため、結果は異なりますが、常に同じ順序でタスクを設定しますが、実行結果は完全に異なることに注意してください。



また、かなり単純なタスクのコルーチンにも注意してください。 asyncioでは、非ブロッキングタスクを実装する際に魔法はないことを理解することが重要です。 実装中、asyncioは標準ライブラリに個別に存在しました。 残りのモジュールはブロッキング機能のみを提供しました。 concurrent.futuresモジュールを使用して、スレッドまたはプロセスでブロッキングタスクをラップし、asyncioで使用するフューチャーを取得できます。 GitHubにはそのような例いくつかあります

これはおそらくasyncioを使用するときの主な欠点ですが、この問題の解決に役立つライブラリが既にいくつかあります。



最も一般的なブロッキングタスクは、HTTP要求を使用してデータを取得することです。 GitHubで公開イベントに関する情報を受信する例を使用して、優れたaiohttpライブラリを操作してましょう。



 import time import urllib.request import asyncio import aiohttp URL = 'https://api.github.com/events' MAX_CLIENTS = 3 def fetch_sync(pid): print('Fetch sync process {} started'.format(pid)) start = time.time() response = urllib.request.urlopen(URL) datetime = response.getheader('Date') print('Process {}: {}, took: {:.2f} seconds'.format( pid, datetime, time.time() - start)) return datetime async def fetch_async(pid): print('Fetch async process {} started'.format(pid)) start = time.time() response = await aiohttp.request('GET', URL) datetime = response.headers.get('Date') print('Process {}: {}, took: {:.2f} seconds'.format( pid, datetime, time.time() - start)) response.close() return datetime def synchronous(): start = time.time() for i in range(1, MAX_CLIENTS + 1): fetch_sync(i) print("Process took: {:.2f} seconds".format(time.time() - start)) async def asynchronous(): start = time.time() tasks = [asyncio.ensure_future( fetch_async(i)) for i in range(1, MAX_CLIENTS + 1)] await asyncio.wait(tasks) print("Process took: {:.2f} seconds".format(time.time() - start)) print('Synchronous:') synchronous() print('Asynchronous:') ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 1d-async-fetch-from-server-asyncio-await.py Synchronous: Fetch sync process 1 started Process 1: Wed, 17 Feb 2016 13:10:11 GMT, took: 0.54 seconds Fetch sync process 2 started Process 2: Wed, 17 Feb 2016 13:10:11 GMT, took: 0.50 seconds Fetch sync process 3 started Process 3: Wed, 17 Feb 2016 13:10:12 GMT, took: 0.48 seconds Process took: 1.54 seconds Asynchronous: Fetch async process 1 started Fetch async process 2 started Fetch async process 3 started Process 3: Wed, 17 Feb 2016 13:10:12 GMT, took: 0.50 seconds Process 2: Wed, 17 Feb 2016 13:10:12 GMT, took: 0.52 seconds Process 1: Wed, 17 Feb 2016 13:10:12 GMT, took: 0.54 seconds Process took: 0.54 seconds
      
      





ここでは、いくつかの点に注意する価値があります。



まず、時間差-非同期呼び出しを使用する場合、リクエストを同時に実行します。 前に述べたように、それぞれが制御を次へ移し、完了時に結果を返しました。 つまり、実行速度は0.54秒だけかかった最も遅い要求の実行時間に直接依存します。 いいですね



第二に、コードがどれだけ同期的に見えるか。 これは本質的に同じことです! 主な違いは、クエリの実行、作成、およびタスクの完了を待機するためのライブラリの実装に関連しています。



競争力の創出



これまで、コルーチンから結果を作成および取得し、一連のタスクを作成してその完了を待機するための唯一の方法を使用してきました。 ただし、いくつかの方法でコルーチンを実行して結果を生成するように計画できます。 GETリクエストの結果を受け取ったときに処理する必要がある状況を想像してください。 実際、実装は前のものと非常に似ています:



 import time import random import asyncio import aiohttp URL = 'https://api.github.com/events' MAX_CLIENTS = 3 async def fetch_async(pid): start = time.time() sleepy_time = random.randint(2, 5) print('Fetch async process {} started, sleeping for {} seconds'.format( pid, sleepy_time)) await asyncio.sleep(sleepy_time) response = await aiohttp.request('GET', URL) datetime = response.headers.get('Date') response.close() return 'Process {}: {}, took: {:.2f} seconds'.format( pid, datetime, time.time() - start) async def asynchronous(): start = time.time() futures = [fetch_async(i) for i in range(1, MAX_CLIENTS + 1)] for i, future in enumerate(asyncio.as_completed(futures)): result = await future print('{} {}'.format(">>" * (i + 1), result)) print("Process took: {:.2f} seconds".format(time.time() - start)) ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 2a-async-fetch-from-server-as-completed-asyncio-await.py Fetch async process 1 started, sleeping for 4 seconds Fetch async process 3 started, sleeping for 5 seconds Fetch async process 2 started, sleeping for 3 seconds >> Process 2: Wed, 17 Feb 2016 13:55:19 GMT, took: 3.53 seconds >>>> Process 1: Wed, 17 Feb 2016 13:55:20 GMT, took: 4.49 seconds >>>>>> Process 3: Wed, 17 Feb 2016 13:55:21 GMT, took: 5.48 seconds Process took: 5.48 seconds
      
      





インデントとタイミングを見てください-すべてのタスクを同時に起動しましたが、完了順に処理されました。 この場合のコードはわずかに異なります。コルーチンをリストにパックします。各コルーチンは実行のためにすでに準備されています。 as_completed関数は、実行時にコルーチンの結果を返す反復子を返します。 本当にかっこいい?! ところで、 as_completedwaitは、 concurrent.futuresパッケージの関数です。



もう1つの例は、IPアドレスを知りたい場合です。 これには多くのサービスがありますが、プログラムの時点でどのサービスが利用可能になるかはわかりません。 各リストを順番にポーリングする代わりに、すべてのクエリを競合して実行し、最初に成功したクエリを選択できます。



さて、このために、私たちのお気に入りの関数waitには特別なパラメーターreturn_whenがあります。 これまで、 待機が返すものを無視しました。 並列化されたタスクのみ。 しかし、コルーチンから結果を取得する必要があるため、完了済みおよび保留中の先物のセットを使用します。



 from collections import namedtuple import time import asyncio from concurrent.futures import FIRST_COMPLETED import aiohttp Service = namedtuple('Service', ('name', 'url', 'ip_attr')) SERVICES = ( Service('ipify', 'https://api.ipify.org?format=json', 'ip'), Service('ip-api', 'http://ip-api.com/json', 'query') ) async def fetch_ip(service): start = time.time() print('Fetching IP from {}'.format(service.name)) response = await aiohttp.request('GET', service.url) json_response = await response.json() ip = json_response[service.ip_attr] response.close() return '{} finished with result: {}, took: {:.2f} seconds'.format( service.name, ip, time.time() - start) async def asynchronous(): futures = [fetch_ip(service) for service in SERVICES] done, pending = await asyncio.wait( futures, return_when=FIRST_COMPLETED) print(done.pop().result()) ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 2c-fetch-first-ip-address-response-await.py Fetching IP from ip-api Fetching IP from ipify ip-api finished with result: 82.34.76.170, took: 0.09 seconds Unclosed client session client_session: <aiohttp.client.ClientSession object at 0x10f95c6d8> Task was destroyed but it is pending! task: <Task pending coro=<fetch_ip() running at 2c-fetch-first-ip-address-response.py:20> wait_for=<Future pending cb=[BaseSelectorEventLoop._sock_connect_done(10)(), Task._wakeup()]>>
      
      





どうしたの? 最初のサービスは正常に応答しましたが、ログに警告がありました!



実際、2つのタスクの実行を開始しましたが、最初の結果の後にサイクルを残し、2番目のコルーチンはまだ実行されていました。 Asyncioはバグだと考え、警告してくれました。 おそらくあなた自身のためにクリーンアップする価値があり、明らかに不要なタスクを殺します。 どうやって? 聞いてくれてうれしい。



未来州





すべてがとてもシンプルです。 futurがdone状態になったら、そこから実行結果を取得できます。 保留中および実行中の状態では、このような操作はInvalidStateError例外をスローします。canelledの場合、 CancelledErrorがスローされ、最後に、コルーチン自体で例外が発生した場合は、 例外が呼び出されたときと同じように再びスローれます。 しかし、私の言葉を信じないでください



donecanceled、またはrunningメソッドを使用して未来の状態を見つけることができますが、 doneの場合、 resultを呼び出すと、期待される結果と操作中に発生した例外の両方が返されることを忘れないでください。 先物実行をキャンセルするキャンセルメソッドがあります これは例を修正するのに適しています。



 from collections import namedtuple import time import asyncio from concurrent.futures import FIRST_COMPLETED import aiohttp Service = namedtuple('Service', ('name', 'url', 'ip_attr')) SERVICES = ( Service('ipify', 'https://api.ipify.org?format=json', 'ip'), Service('ip-api', 'http://ip-api.com/json', 'query') ) async def fetch_ip(service): start = time.time() print('Fetching IP from {}'.format(service.name)) response = await aiohttp.request('GET', service.url) json_response = await response.json() ip = json_response[service.ip_attr] response.close() return '{} finished with result: {}, took: {:.2f} seconds'.format( service.name, ip, time.time() - start) async def asynchronous(): futures = [fetch_ip(service) for service in SERVICES] done, pending = await asyncio.wait( futures, return_when=FIRST_COMPLETED) print(done.pop().result()) for future in pending: future.cancel() ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 2c-fetch-first-ip-address-response-no-warning-await.py Fetching IP from ipify Fetching IP from ip-api ip-api finished with result: 82.34.76.170, took: 0.08 seconds
      
      





シンプルで正確な結論は、私が大好きなものです!



futureを処理するために追加のロジックが必要な場合は、状態が完了したときに呼び出されるコールバックを接続できます。 これは、一部の結果をその値で再定義する必要があるテストで役立ちます。



例外処理



asyncioは、管理された読み取り可能な同時実行コードの記述に関するもので、例外を処理する際に非常に顕著です。 例に戻って説明します。

IP定義によるサービスに対するすべての要求が同じ結果を返すことを確認したいとします。 ただし、そのうちの1つはオフラインであり、応答しない場合があります。 tryを適用するだけです...いつもの場合を除きます:



 from collections import namedtuple import time import asyncio import aiohttp Service = namedtuple('Service', ('name', 'url', 'ip_attr')) SERVICES = ( Service('ipify', 'https://api.ipify.org?format=json', 'ip'), Service('ip-api', 'http://ip-api.com/json', 'query'), Service('borken', 'http://no-way-this-is-going-to-work.com/json', 'ip') ) async def fetch_ip(service): start = time.time() print('Fetching IP from {}'.format(service.name)) try: response = await aiohttp.request('GET', service.url) except: return '{} is unresponsive'.format(service.name) json_response = await response.json() ip = json_response[service.ip_attr] response.close() return '{} finished with result: {}, took: {:.2f} seconds'.format( service.name, ip, time.time() - start) async def asynchronous(): futures = [fetch_ip(service) for service in SERVICES] done, _ = await asyncio.wait(futures) for future in done: print(future.result()) ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 3a-fetch-ip-addresses-fail-await.py Fetching IP from ip-api Fetching IP from borken Fetching IP from ipify ip-api finished with result: 85.133.69.250, took: 0.75 seconds ipify finished with result: 85.133.69.250, took: 1.37 seconds borken is unresponsive
      
      





コルーチンの実行中に発生した例外も処理できます。



 from collections import namedtuple import time import asyncio import aiohttp import traceback Service = namedtuple('Service', ('name', 'url', 'ip_attr')) SERVICES = ( Service('ipify', 'https://api.ipify.org?format=json', 'ip'), Service('ip-api', 'http://ip-api.com/json', 'this-is-not-an-attr'), Service('borken', 'http://no-way-this-is-going-to-work.com/json', 'ip') ) async def fetch_ip(service): start = time.time() print('Fetching IP from {}'.format(service.name)) try: response = await aiohttp.request('GET', service.url) except: return '{} is unresponsive'.format(service.name) json_response = await response.json() ip = json_response[service.ip_attr] response.close() return '{} finished with result: {}, took: {:.2f} seconds'.format( service.name, ip, time.time() - start) async def asynchronous(): futures = [fetch_ip(service) for service in SERVICES] done, _ = await asyncio.wait(futures) for future in done: try: print(future.result()) except: print("Unexpected error: {}".format(traceback.format_exc())) ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 3b-fetch-ip-addresses-future-exceptions-await.py Fetching IP from ipify Fetching IP from borken Fetching IP from ip-api ipify finished with result: 85.133.69.250, took: 0.91 seconds borken is unresponsive Unexpected error: Traceback (most recent call last): File “3b-fetch-ip-addresses-future-exceptions.py”, line 39, in asynchronous print(future.result()) File “3b-fetch-ip-addresses-future-exceptions.py”, line 26, in fetch_ip ip = json_response[service.ip_attr] KeyError: 'this-is-not-an-attr'
      
      





完了を待たずにタスクを開始するのは間違いであるように、未知の例外を取得すると、出力にトレースが残ります。



 from collections import namedtuple import time import asyncio import aiohttp Service = namedtuple('Service', ('name', 'url', 'ip_attr')) SERVICES = ( Service('ipify', 'https://api.ipify.org?format=json', 'ip'), Service('ip-api', 'http://ip-api.com/json', 'this-is-not-an-attr'), Service('borken', 'http://no-way-this-is-going-to-work.com/json', 'ip') ) async def fetch_ip(service): start = time.time() print('Fetching IP from {}'.format(service.name)) try: response = await aiohttp.request('GET', service.url) except: print('{} is unresponsive'.format(service.name)) else: json_response = await response.json() ip = json_response[service.ip_attr] response.close() print('{} finished with result: {}, took: {:.2f} seconds'.format( service.name, ip, time.time() - start)) async def asynchronous(): futures = [fetch_ip(service) for service in SERVICES] await asyncio.wait(futures) # intentionally ignore results ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous()) ioloop.close()
      
      





 $ python3 3c-fetch-ip-addresses-ignore-exceptions-await.py Fetching IP from ipify Fetching IP from borken Fetching IP from ip-api borken is unresponsive ipify finished with result: 85.133.69.250, took: 0.78 seconds Task exception was never retrieved future: <Task finished coro=<fetch_ip() done, defined at 3c-fetch-ip-addresses-ignore-exceptions.py:15> exception=KeyError('this-is-not-an-attr',)> Traceback (most recent call last): File “3c-fetch-ip-addresses-ignore-exceptions.py”, line 25, in fetch_ip ip = json_response[service.ip_attr] KeyError: 'this-is-not-an-attr'
      
      





出力は、asyncioからの非難メッセージを除いて、前の例と同じに見えます。



タイムアウト



しかし、IPに関する情報がそれほど重要でない場合はどうでしょうか? これは、この部分がオプションになるようなある種の複合的な回答を補完するものです。 この場合、ユーザーを待たせません。 理想的には、タイムアウトを設定してIPを計算し、その後、この情報がなくても、ユーザーに答えを返します。



繰り返しますが、 待機には適切な引数があります。



 import time import random import asyncio import aiohttp import argparse from collections import namedtuple from concurrent.futures import FIRST_COMPLETED Service = namedtuple('Service', ('name', 'url', 'ip_attr')) SERVICES = ( Service('ipify', 'https://api.ipify.org?format=json', 'ip'), Service('ip-api', 'http://ip-api.com/json', 'query'), ) DEFAULT_TIMEOUT = 0.01 async def fetch_ip(service): start = time.time() print('Fetching IP from {}'.format(service.name)) await asyncio.sleep(random.randint(1, 3) * 0.1) try: response = await aiohttp.request('GET', service.url) except: return '{} is unresponsive'.format(service.name) json_response = await response.json() ip = json_response[service.ip_attr] response.close() print('{} finished with result: {}, took: {:.2f} seconds'.format( service.name, ip, time.time() - start)) return ip async def asynchronous(timeout): response = { "message": "Result from asynchronous.", "ip": "not available" } futures = [fetch_ip(service) for service in SERVICES] done, pending = await asyncio.wait( futures, timeout=timeout, return_when=FIRST_COMPLETED) for future in pending: future.cancel() for future in done: response["ip"] = future.result() print(response) parser = argparse.ArgumentParser() parser.add_argument( '-t', '--timeout', help='Timeout to use, defaults to {}'.format(DEFAULT_TIMEOUT), default=DEFAULT_TIMEOUT, type=float) args = parser.parse_args() print("Using a {} timeout".format(args.timeout)) ioloop = asyncio.get_event_loop() ioloop.run_until_complete(asynchronous(args.timeout)) ioloop.close()
      
      







また、スクリプトの起動行にタイムアウト引数を追加して、リクエストに処理時間がある場合に何が起こるかを確認しました。 また、スクリプトがすぐに終了するのを防ぐために、ランダムな遅延を追加しました。そして、それが正確にどのように機能するかを理解する時が来ました。



 $ python 4a-timeout-with-wait-kwarg-await.py Using a 0.01 timeout Fetching IP from ipify Fetching IP from ip-api {'message': 'Result from asynchronous.', 'ip': 'not available'}
      
      





 $ python 4a-timeout-with-wait-kwarg-await.py -t 5 Using a 5.0 timeout Fetching IP from ip-api Fetching IP from ipify ipify finished with result: 82.34.76.170, took: 1.24 seconds {'ip': '82.34.76.170', 'message': 'Result from asynchronous.'}
      
      





おわりに



Asyncioは、私のPythonへの既に大きな愛を強化しました。 正直に言うと、Tornadoでコルーチンに出会ったとき、私はコルーチンに恋をしましたが、asyncioは競争力を実装するためにコルーチンと他のライブラリから最高の結果を得ました。 そして、主なI / Oサイクルを使用できるように特別な努力が払われました。 そのため、 TornadoまたはTwistedを使用する場合、asyncio用に設計されたコードを含めることができます。



前述したように、主な問題は、標準ライブラリが非ブロッキング動作をまだサポートしていないことです。 また、これまでのところ、多くの一般的なライブラリは同期スタイルでのみ動作し、競争力を使用するライブラリはまだ若くて実験的です。 ただし、その数は増え続けています。



このチュートリアルで、asyncioを使用することのすばらしさをお見せしたいと思います。何らかの理由でpython 2.7で動けなくなった場合、このテクノロジーによってpython 3に切り替えることができます。 確かなことは1つです。Pythonの未来は完全に変わりました。



翻訳者から:
元の記事は2016年2月20日に公開されましたが、この間に多くのことが起こりました。 Python 3.6がリリースされ、最適化に加えて、asyncioが改善され、APIが安定状態になりました。 Postgres、Redis、Elasticsearchなどを操作するためのライブラリが非ブロックモードでリリースされました。 新しいフレームワークでさえSanicで、Flaskに似ていますが、非同期モードで動作します。 最終的に、イベントループでさえCythonで最適化および書き換えられ、2倍高速になりました。 したがって、このテクノロジーを無視する理由はありません!




All Articles