2015-11-09 66 views
0

我在WinForms應用程序中有這種奇怪的行爲。我從我的主窗體的Form_Shown事件處理程序調用此任務。在新任務中創建WinForm UI控件後等待會導致死鎖?

Task.Run(() => WatchHistory(CancellationTokenSource.Token)); 

函數定義如下。 FinishedRequestItem對象是一個WinForms UI控件。它在等待Task.Delay時死鎖。我注意到一旦創建了FinishedRequestItem對象,就會創建一個新的同步上下文。該上下文與主窗體UI的上下文不同。

private async void WatchHistory(CancellationToken cancellationToken) 
    { 
     while (!cancellationToken.IsCancellationRequested) 
     { 
      var completedRequests = await processContext.ProcessRequests.ToListAsync(cancellationToken); 
      if (completedRequests.Any()) 
      { 
       FinishedRequestItem entry = Program.Container.GetInstance<FinishedRequestItem>(); 

      } 

      await Task.Delay(CurrentHistoryRefreshMilliseconds, cancellationToken); 
     } 

    } 

但是,如果我使用Invoke方法創建UI控件,如下所示它不會死鎖。我很好奇爲什麼?由於該任務沒有在UI上下文中運行,因此可以防止Task.Delay在上面的代碼中恢復?

while (!cancellationToken.IsCancellationRequested) 
     { 
      var completedRequests = await processContext.ProcessRequests.ToListAsync(cancellationToken); 
      if (completedRequests.Any()) 
      { 

       FinishedRequestItem entry = null; 

       this.Invoke((Action)delegate() 
       { 
        entry = Program.Container.GetInstance<FinishedRequestItem>(); 
       }); 

      } 

      await Task.Delay(CurrentHistoryRefreshMilliseconds, cancellationToken); 
     } 
+2

您應該只在UI線程上創建UI控件。你也不應該是'async void',而應該使用'async Task'。 –

+0

我在我更新的代碼中使用異步任務,我知道它有一個更好的模式。但問題依然存在。我認爲它與Task.Delay嘗試在等待它的同一個線程中恢復有關。 – justcoding124

回答

2

首先,你應該在每一個地方使用Task.ConfigureAwait(false)你不想await恢復在當前同步上下文。如果您撥打Task.Delay電話,您就不會遇到這個問題(但很可能是另一個問題)。

但是,如果我使用Invoke方法創建UI控件,如下所示它不會死鎖。我很好奇爲什麼?由於該任務沒有在UI上下文中運行,因此可以防止Task.Delay在上面的代碼中恢復?

事實上,你使用它的方式,將Task.Delay嘗試恢復在無論當前同步上下文是,當你調用它。這是重點。通常線程池線程沒有同步上下文(或者使用缺省的有效的不做任何同步的線程)。但默認情況下,每個System.Windows.Forms.Control都會在其構造函數中安裝(設置爲SynchronizationContext.Current)至WindowsFormsSynchronizationContext。通話

Program.Container.GetInstance<FinishedRequestItem>() 

後,因此,當前同步上下文更改,然後Task.Delay試圖恢復在這種情況下,當然也不能因爲在該線程運行沒有消息泵(這是需要WindowsFormsSynchronizationContext正常運行)。

一般而言,您不應在非UI線程上創建UI元素。