2017-12-30 45 views
0

ほとんどの場合、非同期コードのブロックは不承認となります。Thread.Sleep()とTask.Yield()で非同期でブロックする

しかし、WPFアプリケーションでバックグラウンドタスクを実行しています。 ユーザーがウィンドウを閉じると、このタスクはキャンセルされ、キャンセルとその後のクリーンアップが完了するまでウィンドウの終了が遅延されます。その間に、それ以上のユーザー入力はブロックする必要があります。

しよう&を検索するビットの後、私は次のコードを思い付いた:

ViewModel.cs:

async void RunTaskAsync() 
{ 
    _cancelTknSource = new CancellationTokenSource(); 
    try { 
     await Task.Run(/*...long running calculation...*/, _cancelTknSource.Token); 
    } 
    catch (OperationCanceledException) { } 
    finally { 
     /*...cleanup...*/ 
     _cancelTknSource.Dispose(); 
     _cancelTknSource = null;    
    } 
} 

async Task CancelAsync() 
{ 
    _cancelTknSource?.Cancel(); 
    while (_cancelTknSource != null) { 
     Thread.Sleep(10); 
     await Task.Yield(); //prevents dead-lock 
    } 
} 

Window.xaml.cs:

async void Window_Closing(object sender, CancelEventArgs e) 
{ 
    e.Cancel = true; 
    await DataContext.CancelAsync(); 
    Close(); 
} 

(すべてのこれらのメソッドはUIスレッドから呼び出されます)

これは良い方法ですか? そうでない場合、代替案は何ですか?

+2

ようこそ。私はStackExchangeネットワーク上で、この質問が兄弟館のサイト_Code Review_(なぜあなたの問題は問題ではなくデザインなので)で尋ねられるのだろうか?幸運 – MickyD

+1

Task.Yield()はデッドロックを修正せず、UIスレッドが通知を処理できなくなるという基本的な問題を解決しません。 WPFのアドバイスは基本的に[Winformsとは異なる](https://stackoverflow.com/a/1732361/17034)ではありません。 –

+0

"Task.Runを待つ(/ * ...長時間実行中の計算... * /、_ cancelTknSource.Token);"あなたがこの仕事を待っているならば。トークンのポイントは何ですか? – efekctive

答えて

0

エラー処理やクリーンアップ(例:_cancelTknSourceと_runningTaskにnullを設定)がない場合は、次のコードを実行する必要があります。しかし、Window_Closingと実際のウィンドウの閉じ方の間でいくつかのUI機能をブロックする必要があります。

async void RunTaskAsync() 
{ 
    using(_cancelTknSource = new CancellationTokenSource()) 
    { 
     _runningTask =Task.Run(/*...long running calculation...*/, _cancelTknSource.Token); 
     await _runningTask; 
    } 
} 

Task CancelAsync() 
{ 
    _cancelTknSource?.Cancel(); 
    return _runningTask; 
} 
Window.xaml.cs: 

async void Window_Closing(object sender, CancelEventArgs e) 
{ 
    e.Cancel = true; 
    await DataContext.CancelAsync(); 
    Close(); 
} 
+0

私には、両方のメソッドが同じタスクを待っているので、Close()がRunTaskAsyncの残りのRunTaskAsyncの前または後に実行されるかどうかはわかりません... 多分良いでしょうRunTaskAsync()をvoidからTaskに変更し、これを_runningTaskに保存してください。(Peterが提案したように) – Cornix

+0

@Cornixは同じタスクで複数の 'await'を参照しています[既に実行中のタスクや実行されたタスクを待っているとどうなりますか? //stackoverflow.com/a/32434481/585968) – MickyD

関連する問題