2016-09-01 3 views
1

カスタムシングルスレッドGUIライブラリ(またはイベントループを持つもの)を作成しているとします。私が理解したところでは、async/await、または通常のTPL継続を使用すると、それらはすべてTaskScheduler.Current(またはSynchronizationContext.Current)にスケジュールされます。すべての継続を自分のシングルスレッドイベントループで処理できるようにカスタムSynchronizationContextを作成するにはどうすればよいですか?

問題は、継続がライブラリのシングルスレッド部分にアクセスしたいということです。つまり、同じイベントループで実行する必要があります。例えば、簡単なゲームループ与えられ、イベントは次のように処理される可能性があります:

// All continuation calls should be put onto this queue 
Queue<Event> events; 

// The main thread calls the `Update` method continuously on each "frame" 
void Update() { 
    // All accumulated events are processed in order and the queue is cleared 
    foreach (var event : events) Process(event); 

    events.Clear(); 
} 

は今私の仮定が正しいとTPLはSynchronizationContext.Currentを使用して、アプリケーション内の任意のコードがこのような何かを行うことができるはず与えられました

async void Foo() { 
    someLabel.Text = "Processing"; 

    await BackgroundTask(); 

    // This has to execute on the main thread 
    someLabel.Text = "Done"; 
} 

これは私に質問につながります。 私は自分自身のスレッドで継続を処理できるカスタムSynchronizationContextを実装するにはどうすればよいですか?これは正しいアプローチですか?

答えて

7

カスタムを実装するSynchronizationContextは、世界で最も簡単なものではありません。私は、オープンソースのシングルスレッド実装hereを使用しています。これを出発点として使用できます(または、メインループの代わりに使用することもできます)。デフォルトでは

それが完全に完了すると、AsyncContext.Runは(AsyncContextはカスタムSynchronizationContextを使用するので、async voidメソッドを待つことができるだけでなく、定期的な非同期/同期コードである)の単一実行するためのデリゲートとリターンを取ります。

AsyncContext.Run(async() => await DoSomethingAsync()); 

あなたはより多くの柔軟性をしたい場合は、(「出口フレーム」のような)いくつかの外部信号まで生きコンテキストを維持するためにAsyncContext高度なメンバーを(これらはインテリセンスに表示されませんが、彼らはそこにある)を使用することができます:

using (var context = new AsyncContext()) 
{ 
    // Ensure the context doesn't exit until we say so. 
    context.SynchronizationContext.OperationStarted(); 

    // TODO: set up the "exit frame" signal to call `context.SynchronizationContext.OperationCompleted()` 
    // (note that from within the context, you can alternatively call `SynchronizationContext.Current.OperationCompleted()` 

    // Optional: queue any work you want using `context.Factory`. 

    // Run the context; this only returns after all work queued to this context has completed and the "exit frame" signal is triggered. 
    context.Execute(); 
} 

AsyncContextRunExecuteが実行されている間、現在のSynchronizationContextを交換するが、彼らは返す前にその電流として元のコンテキストを保存し、設定します。これにより、ネストされた方法でうまく動作することができます(「フレーム」など)。

(「フレーム」とは、一種のWPFライクなディスパッチャフレームを意味します)。

+0

あなたは私よりも複雑な問題を解決しているようです。私はネスティングをしていません。フレームでは、レンダリングする単一の呼び出しを意味し、キャンバス上に単一のフレームをレンダリングします。私は他の同期のコンテキストを持っていないでしょう、ただ全体のプログラムのためのものです。私は少し物事を単純化する必要がありますか? –

+0

@JakubArnold:はい、入れ子の部分を無視することができます。 "Exitプログラム"要求が出るまで、メインループを続けるためには、まだ 'OperationStarted'を使う必要があります。 –

+0

しかし、これはSynchronizationContextがループを実行していると仮定しています。キューを処理するためにコンテキストを手動で呼びたいのであれば、 'Post'をオーバーライドしてキューに格納するだけで十分でしょうか? –

関連する問題