2016-09-30 10 views
0

と.NET 4.6.1をターゲットとC#のテストプロジェクトは、私は、次の動作を取得:ここC#は(REPROテストで)のSynchronizationContextをクリア非同期メソッドで待つ

[TestClass] 
public class AwaitTests 
{ 
    [TestMethod] 
    public void AsyncRemovingSyncContext_PartialFail() 
    { 
     Log("1"); 
     SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); 
     Log("2"); 
     HasAwait().Wait(); // The continuation in this method is on wrong thread 
     Log("5"); 
     Assert.IsNotNull(SynchronizationContext.Current); 
    } 

    [TestMethod] 
    public async Task AsyncRemovingSyncContext_Fail() 
    { 
     Log("1"); 
     SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); 
     Log("2"); 
     await HasAwait(); 
     Log("5"); // Issue is here - Sync Context is now null 
     Assert.IsNotNull(SynchronizationContext.Current); 
    } 

    public async Task HasAwait() 
    { 
     Log("3"); 
     await Task.Delay(300); 
     Log("4"); 
    } 

    private void Log(string text) 
    { 
     Console.WriteLine($"{text} - Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} - {SynchronizationContext.Current}"); 
    } 
} 

が出力されます。

AsyncRemovingSyncContext_PartialFail
1 - スレッド7 -
2 - スレッド7 - System.Threading.SynchronizationContext
3 - スレッド7 - System.Th reading.SynchronizationContext
4 - 糸8 -
5 - スレッド7 - System.Threading.SynchronizationContext

AsyncRemovingSyncContext_Error
1 - スレッド7 -
2 - スレッド7 - System.Threading.SynchronizationContext
3 - スレッド7 - System.Threading.SynchronizationContext
4スレッド8 -
5スレッド8 -
- アサルト例外スロー

私は他のテストを行いましたが、これまでの方法ではawaitキーワードの存在と同期コンテキストのクリアとの間に100%の相関があります。これには、非同期ラムダが含まれます。

awaitが発生するとすぐに同期コンテキストが削除されるように見えるため、これは重要です。つまり、同じメソッドで2つのawaitを実行すると、2番目の継続はスレッドプールでのみ実行されます(同期コンテキストがない場合のデフォルトの動作)。

これはフレームワーク/コンパイラのバグですか、何か間違っていますか?詳細については


、私は誰かが一つの形態または別に尋ねると確信しているので、私は私がのためにasyncawait \サポートを有効にしたいのですが、私ができる場合、私は唯一のことを行うことができますActive Objectを持っていますお返事は私のActiveObjectSynchronizationContextに発送されます。現時点では、それはクリアされているわけではありません。

私は既にthis question(と同様のUIコンテキスト4.0バグについて)を見てきましたが、4.6.1を実行してから非UIスレッドを使用しているので関連しません。

また、this other questionのアドバイスに従い、同期コンテキストがCreateCopyを実装していることを確認しましたが、テストプロファイリングからメソッドが呼び出されていないことがわかります。

+1

_ "私が何か間違ったことをやっている?" _ - それはあなたが[基本 'SynchronizationContext'クラスを考えている理由を説明するだろう場合には役立つだろう](http://referencesource.microsoft.com/#mscorlib/system/threading/synchronizationcontext.cs,8b34a86241c7b423)は、ここで何をしているのか正確には何もしていないはずです。 [ドキュメント](https://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext(v = vs.110).aspx)は完全にわかりやすいようです:_ "SynchronizationContextクラスはベースです同期を持たないフリースレッドのコンテキストを提供するクラスです。 "_ –

+0

スレッドプールスレッド(' SynchronizationContext'クラスの動作)で継続が戻ってくるわけではありませんでしたが、同期コンテキストを設定していましたが、私は待っているし、同期コンテキストはnullです。私は最初にカスタムSynchronizationContextを使用してこの動作に気付きました。このSynchronizationContextはnullになり、 'Post'コマンドが送られなくなりました。私が見ていたエラーの最小の実例として、デフォルトの 'SynchronizationContext'クラスを使用しました。 –

+2

_ "同期コンテキストを設定した後、同期コンテキストがヌル" _ - コンテキストはスレッドごとです(ドキュメント化されています)。コンテキストは「nullになりませんでした」。あなたは、あなたが設定したところから別のスレッドで実行するだけで、期待通りに実行します。また、そのスレッドのコンテキストは、期待通りにnullでした。 –

答えて

0

@StephenClearyがコメントで述べたように、実際のコードでは、非同期メソッドで同期コンテキストを設定していましたが、動作すると思っていたが、明らかにそれはありませんステートマシンは、メソッドの開始時にコンテキストを把握していると推測していますか?)。

解決策は、非同期メソッドで同期コンテキストを最初に設定することです。

間違っ

activeObject.Enqueue(async() => 
    { 
     SynchronizationContext 
      .SetSynchronizationContext(
       new ActiveObjectSynchronizationContext(activeObject)); 

     // do work 

     await Task.Delay(500); 

     // Sync Context now cleared 
    }); 

activeObject.Enqueue(() => 
     SynchronizationContext 
      .SetSynchronizationContext(
       new ActiveObjectSynchronizationContext(activeObject)); 
    ); 

activeObject.Enqueue(async() => 
    { 
     // do work 

     await Task.Delay(500); 

     // Still on Active Object thread 
    }); 
5

new SynchronizationContext()by conventionは、null同期コンテキストと同じです。つまり、スレッドプールのコンテキストです。したがって、これらのテストでの動作は予期しないことではありません。

awaitは正しく動作しています。現在の同期コンテキストが表示されており、そのコンテキストに継続しています。しかし、スレッドプール同期コンテキストは、スレッドプールスレッド上でデリゲートを実行するだけです。nullスレッドプール同期コンテキストのため、SynchronizationContext.Currentnew SynchronizationContextに設定しません。

実際のコードの問題は、ActiveObjectSynchronizationContextがキューに入れられた代理人を実行しているときに自分自身を最新の状態に設定していないことです。デリゲートを実行する直前にを呼び出すのはSynchronizationContext.Postの責任ですが、私が間違っている場合は正しいと思いますが、名前 "Active Object"はシングルスレッドのSTAライクなモデルを暗示しているようです。この場合、Postは設定する必要はありません。適切な現在の同期コンテキストを既に持っている正しいスレッドでデリゲートを実行するだけで済みます。

私が役に立ったら、私は単一のスレッド hereを持っていますが、何らかの(明示的な)メッセージポンピングはしません。

+0

素晴らしいです、スティーブン!確かに、私は 'SetSynchronizationContext'をActive Objectスレッドの最初のコマンドとして1回だけ呼び出していました。同期コンテキストの責任が、「Post」への各呼び出しで同期コンテキストを再設定する責任であることはわかりませんでした。 –

+0

@MattKlein:専用のアクティブオブジェクトスレッドを持っている場合は、スレッドが最初に実行を開始したときに一度だけ設定する必要があります。唯一の問題は、非同期メソッドではなく、同期メソッドで設定する必要があることです。 –

+0

それはチケットだった!私のテストコードでは、非同期メソッドで同期コンテキストを設定していましたが、待つ前に、私は良いと思っていました。明確にしていただき、ありがとうございました、あなたの本とブログのために! –

関連する問題