実践的な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_completedずwaitは、 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がスロヌされ、最埌に、コルヌチン自䜓で䟋倖が発生した堎合は、 䟋倖が呌び出されたずきず同じように再びスロヌされたす。 しかし、私の蚀葉を信じないでください 。

done 、 canceled、たたは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倍高速になりたした。 したがっお、このテクノロゞヌを無芖する理由はありたせん

Source: https://habr.com/ru/post/J337420/


All Articles