2016-08-10 3 views
4

私は新しいasyncコルーチン(Python 3.5で導入されました)を理解しようとしています。Python3 Cooperative Multitaskingで `await`ですか?

1997年、私はAndrew Tanenbaumの書籍Modern Operating Systemsの内容を大まかにカバーした大学のコースに参加しました。

何とかPython3のawaitは私にCooperative Multitaskingを思い出させます。ウィキペディアから

協調的マルチタスク、非プリエンプティブマルチタスクとして知られているが、オペレーティングシステムが別のプロセスに実行中のプロセスからのコンテキストスイッチを開始したことがないているマルチタスクのスタイルです。代わりに、複数のアプリケーションを同時に実行できるように、プロセスは自発的に制御を定期的に、またはアイドル状態のときに行います。このタイプのマルチタスキングは、「協調」と呼ばれています。これは、すべてのプログラムが協調してスケジューリングスキーム全体が機能する必要があるためです。

オペレーティングシステムのようにPythonインタプリタを見ると、 "Cooperative Multitasking"という用語はawaitに当てはまりますか?

しかし、何か不足している可能性があります。

+0

「待っているコルーチン」はややナンセンスです。 asyncioがstdlibモジュールを参照する "asyncio coroutines"を参照するように質問を変更することを提案して、他のコルーチンシステム(gevent、twistedなど)と区別することはできますか? "await"を使用して式のタイプを記述できます。 –

+0

@GaryvanderMerwe「非同期コルーチン」に変更しました。今大丈夫? – guettli

答えて

5

コルーチン関数内では、結果が利用可能になるまで、コルーチンの実行を中断するために、await式を使用してコルーチンの実行を中断することができます。待ち受けプロトコルを ()と定義している限り、オブジェクト を待つことができます(()メソッドを待ちます)。

コルーチンは、別のコルーチンとawaitキーワードを使用して実行を一時停止できます。それが一時停止されている間、コルーチンの状態は維持され、次回起動時に中断したところから再開することができます。 私には協力マルチタスクのように聞こえるでしょう。これを参照してくださいexample

+1

私は、 'await asyncio.sleep(0)'を使ってイベントループへの制御を放棄することができると考えています(例えば、長時間実行する関数の中では、[githubに関するこのディスカッション](https://github.com)を参照してください。/python/asyncio/issues/284など)は、協調的なマルチタスクのように聞こえるものです。 – mgc

3

はい。 Wikipediaによれば:

コルーチンは、特定の場所で実行を中断および再開するための複数のエントリポイントを可能にすることにより、ノンプリエンプティブマルチタスキングためのサブルーチンを一般のコンピュータ・プログラム・コンポーネントです。

3

確かに協調マルチタスクです。

それを証明する小さなプログラムはどうでしょうか。まずasyncio.sleepという共同体で1秒間眠ってから、ブロックtime.sleepで1秒間寝ましょう。スレッドID、コルーチンに費やされた時間、タスクIDを表示しましょう。

import threading 
import asyncio 
import time 

async def async_function(i): 
    started = time.time() 
    print("Id:", i, "ThreadId:", threading.get_ident()) 
    await asyncio.sleep(1) 
    time.sleep(1) 
    print("Id:", i, "ThreadId:", threading.get_ident(), "Time:", time.time() - started) 

async def async_main(): 
    await asyncio.gather(
     async_function(1), 
     async_function(2), 
     async_function(3) 
    ) 

loop = asyncio.get_event_loop() 
loop.run_until_complete(async_main()) 

今度試して見てみましょう:

Id: 3 ThreadId: 140027884312320 
Id: 2 ThreadId: 140027884312320 
Id: 1 ThreadId: 140027884312320 
Id: 3 ThreadId: 140027884312320 Time: 2.002575397491455 
Id: 2 ThreadId: 140027884312320 Time: 3.0038201808929443 
Id: 1 ThreadId: 140027884312320 Time: 4.00504469871521 

予想通り。実行は1つのスレッドのみでした。 asyncio.sleep(1)はノンブロッキングなので、同時にすべてを処理するのに1秒かかりました。 time.sleep(1)はブロックしています(協力しません)ので、残りをブロックします。Id 1は、ID 2が終了するのを待ちます。一方、ID 2は、ID 3が終了するのを待ちます。

C#はasync/awaitも持っていますが、協調的なマルチタスクもありますか?

のは、C#で同じことを試してみましょう:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace AsyncTest 
{ 
    class MainClass { 
     private static async Task AsyncMethod(int id) { 
      var started = DateTime.Now; 
      Console.WriteLine("Id: {0} ThreadId: {1}", id, Thread.CurrentThread.ManagedThreadId); 
      await Task.Delay(1000); 
      Thread.Sleep(1000); 
      Console.WriteLine("Id: {0} ThreadId: {1} Time: {2}", id, Thread.CurrentThread.ManagedThreadId, DateTime.Now - started); 
     } 

     private static async Task MainAsync() 
     { 
      await Task.WhenAll(AsyncMethod(1), AsyncMethod(2), AsyncMethod(3)); 
     } 

     public static void Main (string[] args) { 
      MainAsync().Wait(); 
     } 
    } 
} 

実行それをして...

Id: 1 ThreadId: 1 
Id: 2 ThreadId: 1 
Id: 3 ThreadId: 1 
Id: 2 ThreadId: 7 Time: 00:00:02.0147000 
Id: 3 ThreadId: 8 Time: 00:00:02.0144560 
Id: 1 ThreadId: 6 Time: 00:00:02.0878160 

くそ。スレッドは待ってから別です。コルーチンごとに2秒しかかかりませんでした!どうしましたか?

何も問題ありません。 Pythonとは異なり、C#のasync/awaitには、協調的なマルチタスキングとマルチスレッドの組み合わせがあります。Task.Delay(1000)は実際には非ブロッキングですが、コルーチンが再開すると、例の場合と全く異なるスレッドで再開することができます。コルーチンは3つの異なるスレッドで継続されていたので、Thread.Sleep(1000)は並行してそれらをブロックしました。

C#ではこの動作(SynchronizationContextなど)に影響する可能性のあるものがありますが、これは別のトピックです。