2010-12-02 105 views
1

我对如何在多线程应用程序中使用GUI有点困惑。线程,事件和GUI

我听说有一个叫做UI线程的东西。我认为这是我在应用程序启动时的主要执行线程。

我也听到(虽然我不是100%)在其他(非UI)线程上做UI的东西是一个非常糟糕的主意。

所以,如果我创建一个单独的线程,并且我想在其中调用MyForm myForm = new MyForm(); myForm.ShowDialog();,那么需要做出什么更改才能使其“安全”?

此外,我有一些人告诉我,事件是在不同的线程上分解出来的。 (虽然我不确定我是否相信这一点。)如果他们是,那么我很困惑。我可以打开一个对话框(即在事件myForm.ShowDialog()并没有什么真正可怕的情况发生。(也许这取决于如果事件委托被称为用InvokeBeginInvoke?)

+0

感谢您的所有伟大的答案!我希望我可以选择多个作为“正确”的答案。 – Vaccano 2010-12-02 19:33:13

回答

3

这里是信息,可以帮助你走出的几个位。你在非UI线程上使用UI所说的话不只是一个坏主意,你会得到一个异常。意思是,如果你在主线程中创建一个Form,然后产生一个后台线程来做一些处理,然后想在后台线程中更新表单,它会抛出一个异常。但在你的例子中,在后台线程中创建表单的地方,你应该没问题。不会被炸掉只要您只是触摸相同的用户界面

至于事件,事件处理程序在它们被引发的同一个线程上执行。意思是说,如果你在一个线程上有一个表单,这个表单在另一个引发事件的线程上产生了一些工作,但在这之前,你在表单线程中挂钩了这个事件,你需要小心,不要直接在事件处理程序,因为这些事件处理程序正在后台线程上调用。

最后,从后台线程正确处理UI的方法是通过调用Invoke并传入一个代理来执行所需的UI工作。 HTH

3

在WinForms中,您需要在UI线程上调用UI事物,您总是可以检查您当前正在获取的UI控件的哪个线程为InvokeRequired

void ApplyUiChanges() 
{ 
    if(this.InvokeRequired) 
    { 
     this.Invoke(new Action(ApplyUiChanges)); 
     return; 
    } 

    // UI stuff here... 
} 

在WPF techinic是相似的。但是,而不是使用InvokeRequired你应该问CheckAccess()DispatcherObject(所有UI的控件从中获得)

void ApplyUiChanges() 
{ 
    if (!dispatcherObject.CheckAccess()) 
    {  
     dispatcherObject.Dispatcher.Invoke(DispatcherPriority.Send, new Action(ApplyUiChanges)); 
     return; 
    } 
    // UI stuff here... 
} 

您也可以看看Async CTP,这可能是有用的。但它只是CTP,还没有发布。

处理UI线程通信的另一种方法是使用PostSharp。写(或复制粘贴)GuiThreadAttribute。之后,您将能够使用这样的语义:

[GuiThread] 
void ApplyUiChanges() 
{ 
    // UI stuff here... 
} 
2

在WinForms应用程序中,只有一个线程是UI线程。您不想通过长操作来阻止此线程,以便UI始终能够响应。你也不应该从UI线程以外的任何线程更新任何UI元素。
如果我想从UI执行任何冗长的操作,我总是使用BackgroundWorker。BackgroundWorker的主要优点是它可以报告进度并通过ProgressChanged和RunWorkerCompleted报告它已完成。这两个事件发生在UI线程中,因此您可以安全地更新任何UI元素,而无需使用InvokeRequired和Invoke。

3

从我所经历的,“UI线程”是一个用词不当。没有一个线程可以处理应用程序的所有UI。为了简单起见,在一个线程上使用UI通常是一个好主意,但是没有任何东西阻止您产生另一个线程,并在该线程上创建新的控件并向用户显示它们。重要的是,控制属性仅在上创建的线程上更改为。正如另一个人所提到的,通过查看Control.InvokeRequired属性,可以看到您是否正在该线程中。

如果您所在的线程不是您希望运行的新窗体的线程,而且您不具备在所需线程上创建的控件上下文的豪华感,那么你必须得到对你想要的线程的System.Threading.SynchronizationContext的引用(我通常通过在静态变量中存储来自主线程的System.Threading.SynchronizationContext.Current的引用来实现这一点,但这只能在线程上创建至少一个控件后才能完成)。该对象将允许您在其主线程上运行委托。

我不得不在一个Windows应用程序中执行此操作,该应用程序还托管了一个WCF服务,并且需要从该服务启动UI,但我希望它与该UI的其余部分位于相同的线程中。

HTH, Brian

+0

+1因为用词不当,实在令人困惑 – 2013-03-20 11:40:39