2017-09-13 71 views
1

這是我在WPF.NET中遇到的一個問題。爲了說明這個問題,讓我們來看看下面的類: 爲什麼在WPF中使用ManualResetEvent和異步方法時發生死鎖?

public class TaskRunnerWithProgressFeedback(){ 
    ManualResetEvent _event = new ManualResetEvent(false); 
    public void RunTask(Action action) { 
     _event.Reset(); 
     //Display loading screen 
     RunAsync(action); 
     Console.WriteLine("Load completed."); 
     //Hide loading screen 
     _event.WaitOne(); 
    } 

    private async void RunAsync(Action action) { 
     await Task.Run(() => action.Invoke()); 
     _event.Set(); 
    } 
} 

所以我在這裏有這個類,我會請從UI線程RunTask方法。 例如:

private void Button1_OnClick(object sender , RoutedEventArgs e) { 
    var x = new TaskRunnerWithProgressFeedback(); 
    x.RunTask(()=>{ /*Some time-consuming action*/ }); 
} 

,並點擊Button1的時候,整個程序運行到一個僵持的局面。你對這種情況有任何解釋嗎?

腳註:我需要TaskRunnerWithProgressFeedback班才能做行爲測試。我沒有使用BackgroundWorker,因爲它會打破這些測試。

+1

'WaitOne()'在哪裏? – Sinatr

+1

不要在沒有await的情況下調用'async'方法。除了事件處理程序之外,不要在任何地方使用'async void'方法。 – dymanoid

+0

Sinatr對不起,那是一個意外,只是加了回來。 –

回答

2

一旦您在RunAsync方法中創建的Task完成後,_event.Set()的調用應該在UI線程上執行。

如果在任務完成之前在UI線程上調用_event.WaitOne(),則會發生死鎖。

因爲這時UI線程等待ManualResetEvent進行設置,但也絕不會因爲調用Set()方法的代碼不能在UI線程上,因爲它是由WaitOne()呼叫阻塞執行。

這基本上是如何工作的async/awaitasync方法同步運行,直到碰到並且await,然後返回給調用者。捕獲上下文(在這種情況下爲調度程序線程),然後在相同的調度程序/ UI線程上執行方法的其餘部分,以等待已完成的方法完成後調用方法async

但是,當上下文線程空閒時,方法的其餘部分當然可以不被執行。在這種情況下,它永遠不會是因爲它等待async方法的其餘部分調用Set() =>死鎖。

+0

這是對問題的很好的描述。爲了解決這個問題,你可以使用[AsyncEx library]中的'AsyncManualResetEvent'(https://github.com/StephenCleary/AsyncEx) –

相關問題