我对如何在多线程应用程序中使用GUI感到困惑。
我听说有一个叫UI线程的东西。我假设这是我在应用程序启动时的主要执行线程。
我也听到(虽然我不是100%)在其他(非UI)线程上做UI工作是一个非常糟糕的主意。
所以,如果我创建一个单独的线程并且我想在其中调用MyForm myForm = new MyForm(); myForm.ShowDialog();
,那么我需要做些哪些更改才能“安全”?
另外,我有一些人告诉我事件是在另一个线程上分拆出来的。 (虽然我不确定我是否相信这一点。)如果他们是,那我就很困惑。我可以在一个事件中打开一个对话框(即myForm.ShowDialog()
,并且没有真正可怕的事情发生。(也许这取决于是否使用Invoke
或BeginInvoke
来调用事件代表?)
答案 0 :(得分:3)
以下是一些可以帮助您的信息。你在非UI线程上使用UI的说法不仅仅是一个坏主意,你会得到一个例外。这意味着,如果您在主线程中创建一个Form,然后生成一个后台线程来进行一些处理,然后想要更新该后台线程中的Form,它将抛出异常。但是,在您的示例中,您在后台线程中创建表单的位置应该没问题。我会质疑你的设计,但它不会爆炸因为你只需要触及相同线程的用户界面。
对于事件,事件处理程序在它们引发的同一个线程上执行。这意味着,如果你在一个线程上有一个Form,它会在另一个引发事件的线程上产生一些工作,但在这之前,你在Form线程上挂钩这个事件,你需要注意不要直接触摸UI中的UI。事件处理程序,因为在后台线程上调用了这些事件处理程序。
最后,从后台线程正确操作UI的方法是调用Invoke并传入一个执行所需UI工作的委托。 HTH
答案 1 :(得分:3)
在WinForms中,你需要在UI线程上调用UI-things,你总是可以检查你当前正在获得UI控件InvokeRequired
的线程。
void ApplyUiChanges()
{
if(this.InvokeRequired)
{
this.Invoke(new Action(ApplyUiChanges));
return;
}
// UI stuff here...
}
在WPF技术中是相似的。但是,您应该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 :(得分:3)
从我的经历来看,“UI线程”是用词不当。没有一个线程可以处理应用程序的所有UI。为了简单起见,在一个线程上使用UI通常是一个好主意,但没有什么能阻止您生成另一个线程,并在该线程上创建新控件并将其显示给用户。重要的是控件属性仅在上创建的线程上更改。正如另一个人所提到的,您可以通过查看Control.InvokeRequired属性来查看您当前是否在该线程上。
如果您所在的线程不是您希望运行新表单的线程,并且您没有在所需线程上创建的控件的上下文中获胜,那么您我必须得到你希望它所在的线程的System.Threading.SynchronizationContext的引用(我通常通过在静态变量中存储来自主线程的System.Threading.SynchronizationContext.Current的引用来实现这一点,但是这只能在线程上创建至少一个控件之后才能完成。该对象允许您在其主线程上运行委托。
我必须在同样托管WCF服务的Windows应用程序中执行此操作,并且需要从服务启动UI,但我希望它与UI的其余部分位于同一个线程中。
HTH, 布赖恩
答案 3 :(得分:2)
在WinForms应用程序中,只有一个线程是UI线程。您不希望使用长操作阻止此线程,以便UI始终响应。此外,您不应更新UI线程以外的任何线程中的任何UI元素 如果我想从UI执行任何冗长的操作,我总是使用BackgroundWorker。 BackgroundWorker的主要优点是它可以通过ProgressChanged和RunWorkerCompleted报告进度并报告它已完成。这两个事件发生在UI线程中,因此您可以安全地更新任何UI元素,而无需使用InvokeRequired和Invoke。