2017-10-06 36 views
2

鑑於C#代碼示例運行事件處理程序:如何在目標線程

using System; 
using System.Threading; 
using System.Windows.Forms; 

public class MnFrm : Form 
{ 
    private void MnFrm_Load(Object sender, EventArgs e) 
    { 
     this.WorkCompleted += MnFrm_WorkCompleted; 
    } 

    private void btn_Click(Object sender, EventArgs e) 
    { 
     ThreadPool.QueueUserWorkItem(AsyncMethod); 
    } 

    private void MnFrm_WorkCompleted(Object sender, Boolean e) 
    { 
     MessageBox.Show("Work completed"); 
    } 

    private void AsyncMethod(Object state) 
    { 
     // Do stuff 
     Boolean result = true; // just as an example 
     WorkCompleted?.Invoke(this, result); 
    } 

    private event EventHandler<Boolean> WorkCompleted; 
} 

當用戶點擊該方法AsyncMethod對由線程池管理的另一線程執行的按鈕btn。一段時間後,工作完成,結果通過另一個事件回傳。 此事件處理程序(WorkCompleted)在用於運行AsyncMethod的線程上執行,因爲執行應用程序時會出現「跨線程」異常。

所以問題是如何在UI線程上運行事件處理程序MnFrm_WorkCompleted

+0

正確的解決方案將取決於'AsyncMethod'實際正在做什麼。如果方法訪問某些外部資源,那麼您的代碼可以更改爲在一個線程上有效運行,而不必擔心線程/調用和其他多線程相關問題。 – Fabio

+0

爲什麼使用'QueueUserWorkItem'而不是'var result = await Task.Run(...)'?它在所有支持的.NET版本中都可用,並將所有這些代碼轉換爲正確處理異步操作的* single *行。最早支持的.NET版本是4.5.2。 「等待」是早些時候在4.5中加入的。 4.0的任務 –

+2

即使您需要從另一個線程報告某些內容,請勿使用事件。這是IProgress 界面和'Progresss '類的工作 –

回答

0

如果你把它改成這樣:

this.Invoke(new Action(() => 
{ 
    WorkCompleted?.Invoke(this, result); 
}); 

它將工作。那是因爲該表單包含一個方法Invoke(),它將在創建它的線程上調用該方法。事件的調用只不過是調用委託。

閱讀here形式moet信息。

您可以使用InvokeRequired來確定您是否在正確的線程上,但我認爲這是開銷並使其不易讀。始終使用this.Invoke


BeginInvoke,當'發佈'消息到UI線程時,這是有用的。唯一的問題是,當線程引發許多事件並且UI線程沒有足夠的時間來處理它們時,您看不到有多少人在排隊。


第三種方法是使用隊列。當事件發生時,將一條消息添加到隊列(我將使用List<>並使用窗體計時器來處理隊列。優點是線程不會被UI線程停頓,並在將其添加到隊列後直接繼續。而你的應用程序將會停滯。

2

您可以使用Control.Invoke或Control.BeginInvoke方法在UI線程上調用特定的方法。 試試下面的代碼:

private void MnFrm_WorkCompleted(Object sender, Boolean e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke((Action) (() => MnFrm_WorkCompleted(sender, e))); 
     return; 
    } 
    MessageBox.Show("Work completed"); 
} 

對於調用並BeginInvoke的區別: What's the difference between Invoke() and BeginInvoke()

0

謝謝大家!

回答Jeroen van Langen是相當正確的!有效。實際上,我是這麼做的,但不想發佈解決方案以防止偏見 - 我想看到其他解決方案。

但是,我更喜歡Panagiotis Kanavos的答案,他建議使用var result=await Task.Run(...)。這清楚地清理了代碼!謝謝Panagiotis Kanavos !!!