2017-11-08 22 views
3

簡単な例:2つの無関係なHTTPリクエストを並行して作成する必要があります。それを行う最も簡単な方法は何ですか?私はそれがそのようなことを期待:Pythonのコルーチンの並列非同期IO

async def do_the_job(): 
    with aiohttp.ClientSession() as session: 
     coro_1 = session.get('http://httpbin.org/get') 
     coro_2 = session.get('http://httpbin.org/ip') 
     return combine_responses(await coro_1, await coro_2) 

言い換えれば、私はIO操作を開始し、それらが効果的に並列に実行するように、その結​​果を待ちたいです。これはasyncio.gatherで達成することができます:

async def do_the_job(): 
    with aiohttp.ClientSession() as session: 
     coro_1 = session.get('http://example.com/get') 
     coro_2 = session.get('http://example.org/tp') 
     return combine_responses(*(await asyncio.gather(coro_1, coro_2))) 

次に、私はいくつかの複雑な依存構造を持つようにしたいですか?私はそれらのためのすべての前提条件を持っているときに操作を開始し、結果が必要なときに結果を得たいと思う。ここでは別途イベントループによって管理されているコルーチンとは別のタスクを作るasyncio.ensure_futureを支援します。

async def do_the_job(): 
    with aiohttp.ClientSession() as session: 
     fut_1 = asyncio.ensure_future(session.get('http://httpbin.org/ip')) 
     coro_2 = session.get('http://httpbin.org/get') 
     coro_3 = session.post('http://httpbin.org/post', data=(await coro_2) 
     coro_3_result = await coro_3 
     return combine_responses(await fut_1, coro_3_result) 

は、私はasyncio.ensure_futureかのいずれかを使用する必要があり、それ本当、私の論理フローでコルーチンと並列のノンブロッキングIOを達成するために、ということですasyncio.gather(実際にはasyncio.ensure_futureを使用しています)?あまり「冗長」な方法はありますか?

通常の開発者は、コルーチンを個別のタスクにする必要があり、最適なパフォーマンスを得るためには前述の機能を使用する必要があります。

イベントループで複数のタスクを実行しないでコルーチンを使用することはありますか?

実際のイベントループタスクはどれくらい重いものですか?確かに、彼らはOSのスレッドやプロセスよりも「軽い」。このような仕事の可能な限り少ない数を私はどの程度まで努力すべきですか?

答えて

5

2つの無関係なHTTPリクエストを並行して作成する必要があります。それを行う最も簡単な方法は ですか?

import asyncio 
import aiohttp 


async def request(url): 
    async with aiohttp.ClientSession() as session: 
     async with session.get(url) as resp: 
      return await resp.text() 


async def main(): 
    results = await asyncio.gather(
     request('http://httpbin.org/delay/1'), 
     request('http://httpbin.org/delay/1'), 
    ) 
    print(len(results)) 


loop = asyncio.get_event_loop() 
try: 
    loop.run_until_complete(main()) 
    loop.run_until_complete(loop.shutdown_asyncgens()) 
finally: 
    loop.close() 

はい、あなたはasyncio.gatherまたはasyncio.ensure_futureでタスクを作成すると、並行性を達成することができます。

次に、いくつかの複雑な依存関係が必要ですか?私はそれらのためのすべての前提条件を持っているときに オペレーションを開始し、結果が必要なときに の結果を得たいと思う。

あなたが提供されているコードは、仕事をするだろうが、別のコルーチンに並行フローを分割して、再度asyncio.gatherを使用することがよりよいでしょう:

import asyncio 
import aiohttp 


async def request(url): 
    async with aiohttp.ClientSession() as session: 
     async with session.get(url) as resp: 
      return await resp.text() 


async def get_ip(): 
    return await request('http://httpbin.org/ip') 


async def post_from_get(): 
    async with aiohttp.ClientSession() as session: 
     async with session.get('http://httpbin.org/get') as resp: 
      get_res = await resp.text() 
     async with session.post('http://httpbin.org/post', data=get_res) as resp: 
      return await resp.text() 


async def main(): 
    results = await asyncio.gather(
     get_ip(), 
     post_from_get(), 
    ) 
    print(len(results)) 


loop = asyncio.get_event_loop() 
try: 
    loop.run_until_complete(main()) 
    loop.run_until_complete(loop.shutdown_asyncgens()) 
finally: 
    loop.close() 

は、それは通常、開発者は、コルーチン何を考える必要があること本当です は別のタスクになり、上記の関数を使用して の最適なパフォーマンスを得る必要がありますか?

asyncioを使用しているため、パフォーマンスを得るためにいくつかのジョブを同時に実行する必要がありますか? asyncio.gatherは、「これらのジョブを同時に実行して、結果をより速く」と言う方法です。

パフォーマンスを得るために同時に実行するジョブを考える必要がない場合は、プレーンな同期コードでも問題ありません。

イベント ループに複数のタスクを使用しないでコルーチンを使用することはありますか?

あなたのコードでは、手動でタスクを作成する必要はありません。この回答の両方のスニペットはasyncio.ensure_futureを使用しません。しかし、内部でasyncioはタスクを常に使用します(たとえば、asyncio.gatherはタスク自体を使用しています)。

は、現実の生活の中でどのように「重い」ですイベントループタスク?確かに、彼らは OSスレッドやプロセスより "軽い"です。このようなタスクを最小限に抑えるために、私はどの程度まで を努力すべきですか?

非同期プログラムの主なボトルネックは(ほぼ常に)ネットワークです:あなたはasyncioコルーチン/タスクの数をまったく心配するべきではありません。

+0

これは主に私の質問に回答しています。 'asyncio.gather'とコルーチン関数を使った連鎖や' asyncio.ensure_future'は、コルーチンと並行してIOを行う標準的な方法です。私のサンプルコードでは、誤ってaiohttpを使用していることが分かりました。 –

関連する問題