2016-09-29 10 views
1

WinFormsアプリケーションで作業するドライバを取得しようとしています(WPFに行くことができますが、これはオプションではありません)。興味深い問題。 UIスレッドは、awaitメソッドがドライバで呼び出されたときに永遠にブロックします。ここでは私の問題の非常に単純化されたバージョンですが、これはWindows Formsアプリケーションでのみ発生するようです。WinFormsはUIをブロックせずに非同期メソッドを呼び出す同期メソッドを実行します

private void button_Click(object sender, EventArgs e) 
{ 
    MessageBox.Show(this.TestSynchronous().ToString()); 
} 

// I'm trying to avoid having to change the code below 
private int TestSynchronous() 
{ 
    return this.TestAsync().Result; 
} 

private async Task<int> TestAsync() 
{ 
    await Task.Delay(100); 

    var rand = new Random(); 

    return rand.Next(); 
} 

「TestSynchronous」メソッドに呼び出しをラップして、UIスレッドを永続的にブロックできないようにする方法はありますか?私はどのような方法でも 'Test'メソッドを変更する必要がないようにしようとしていることを忘れないでください。

await works but calling task.Result hangs/deadlocksのような重複したような投稿がありましたが、私の問題と似ていますが、私の問題は同じ方法で解決できません。なぜなら、彼は私がしていない間に非同期メソッドへの直接アクセスを待っているからです。繰り返しますが、私は 'TestSynchronous'メソッドを私に提示するライブラリを呼び出しています。メソッドをラップすることができる方法があるかどうかを知りたいので、ライブラリのどこかでライブラリをブロックすることになりませんコードを変更するためのアクセス権がありません。

+0

よく知られていて、(https://www.bing.com/search?q=c%23+async+result+deadlock)の問題を検索するのは簡単なので、私はあなたがそれを見逃してしまったかどうかはわかりませんあなたの研究中に。将来の質問については、あなたの研究の結果を投稿に追加してください(問題の調査に費やした日数に関係なく、*実証された*研究の欠如は投稿の下落を引き起こす可能性があります)。 –

+0

上記の私の更新版をお読みください。あなたはそれが本質的に同じ問題が発生している間違っているわけではありませんが、自分自身の特定の制限のために、ポストで概説された解決策は私の問題を解決することができません。 – Thermonuclear

答えて

4

ドライバでawaitメソッドが呼び出されると、UIスレッドは永遠にブロックされます。

あなたは私のブログで完全に説明しているcommon deadlock problemに入っています。つまり、awaitは「コンテキスト」を取得し、そのコンテキストを使用してasyncメソッドを再開します。 WinFormsにはUIスレッドで再開するSynchronizationContextがあります。 ブロックブロックResultを呼び出してUIスレッドを呼び出すと、asyncメソッドは再開できず、デッドロックが発生します。

UIスレッドを永久にブロックしないように、 'TestSynchronous'メソッドを呼び出す方法はありますか?

すべてのシナリオで動作し解決策はありません。

最高アプローチはasyncが自然にあなたのコードベースを通じて成長できるようにすることです:

private async Task<int> TestNoLongerSynchronousAsync() 
{ 
    return await this.TestAsync(); 
} 

private async void button_Click(object sender, EventArgs e) 
{ 
    MessageBox.Show((await this.TestNoLongerSynchronousAsync()).ToString()); 
} 

かかわらず、あなたは、これはなると思ういかに難しいかの、それはほぼ確実に同期オーバーを行うよりもはるかに簡単です非同期ハック

あなたが、その後、あなたは私のbrownfield async articleで説明したハックのいずれかを使用することができます(「できない」の非常に強い定義について)awaitを使用できない場合は、次の

  • ブロック(すでに試したが、うまくいかなかった)。
  • スレッドプールスレッド(つまり、Task.Run(() => this.TestAsync()).Result)でブロックします。
  • ネストされたメッセージループをポンピングする(最も危険なものすべて)。

これらはいずれもすべてのシナリオで機能しません。ソリューションがないシナリオがいくつかあります。がんばろう。

+0

OPはあなたの他の答えが役に立たないと言ったので(これは本質的に "ヌル同期化コンテキストでのオペレーションの実行"です)、私はこれが助けになるとは確信していません:) –

+0

いいえ、私が望んでいた答えではありません有益なありがとう、私はコードベースを変更するためにプッシュしなければならないかもしれませんが、おそらく他の方法のいずれかが私のために働かないことを確認するためにもう少し長くプレイします。 – Thermonuclear

0

私は実際にこの問題を解決することができました。その答えは実際にはかなり簡単でした。ここに、この例の修正版があります。

private void button_Click(object sender, EventArgs e) 
{ 
    var task = Task.Run<int>(
      () => this.TestSynchronous()); 

    task.Wait(); 

    MessageBox.Show(task.Result.ToString()); 
} 

効果的に同じことを行うための他の方法がありますが、これは私の問題のために働くようです。面白いのは、以前に(私の質問を投稿する前に)似たような方法で修正しようとしましたが、うまくいかなかったのですが、今ではどうしたのか分かりません。

関連する問題