假设您有一个C#WinForms应用程序进行一些数据处理。 您有一个方法可以从UI线程调用的数据库中检索数据。 然后运行后台线程来执行此任务。 您希望UI继续执行其操作,而不是被锁定和无响应。
如何让后台线程运行并进行处理,然后在返回结果时自动提醒UI线程?
答案 0 :(得分:13)
如果您不使用background worker线程(无论出于何种原因),则必须从您的线程触发由UI线程处理的事件。例如,我有这段代码可以扫描我的mp3,发现每张专辑的火灾和事件,然后是完成(或停止)的另一个事件:
public void Build()
{
FindAlbums(Root);
// Final update
if (Library_Finished != null)
{
Library_Finished(this, null);
}
}
private void FindAlbums(string root)
{
// Find all the albums
string[] folders = Directory.GetDirectories(root);
foreach (string folder in folders)
{
string[] files = Directory.GetFiles(folder, "*.mp3");
if (files.Length > 0)
{
// Add to library - use first file as being representative of the whole album
var info = new AlbumInfo(files[0]);
if (Library_AlbumAdded != null)
{
Library_AlbumAdded(this, new AlbumInfoEventArgs(info));
}
}
FindAlbums(folder);
}
}
然后在UI线程(这是WinForms代码)中:
private void Library_AlbumAdded(object sender, AlbumInfoEventArgs e)
{
if (dataGridView.InvokeRequired)
{
dataGridView.Invoke((MethodInvoker)delegate { AddToGrid(e.AlbumInfo); });
}
else
{
AddToGrid(e.AlbumInfo);
}
}
private void Library_Finished(object sender, EventArgs e)
{
if (dataGridView.InvokeRequired)
{
dataGridView.Invoke((MethodInvoker)delegate { FinalUpdate(); });
}
else
{
FinalUpdate();
}
}
但是,我会建议您调查后台工作线程,因为它会为您完成大量的内务处理。但是,RunWorkerCompleted
事件中需要相同的处理代码来更新UI。
答案 1 :(得分:6)
有几种方法可以做到这一点,但最简单的方法是使用BackgroundWorker。
基本上它有两个代表,DoWork和WorkCompleted。 DoWork在单独的线程上执行,而WorkCompleted回调发生在UI线程上。
以下是更多信息: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
答案 2 :(得分:3)
您可以使用BackgroundWorker在其DoWork事件处理程序中执行时间密集型处理。然后处理RunWorkerComplete事件 - 它将在DoWork方法完成时触发。虽然这一切都在进行,但您的UI线程将很乐意继续运行。
答案 3 :(得分:0)
如果您正在谈论WinForm应用程序,则可以使用表单上的Invoke方法(或表单上的任何控件)对任何UI对象进行更改。您还可以找到有用的InvokeRequired属性
答案 4 :(得分:0)
如果您使用的是.NET 2.0或更高版本,那么使用BackgroundWorker主题可以更轻松地实现这一点。它有自己的RunWorkerCompleted事件,可以满足您的需求。
我强烈推荐BackgroundWorker。它具有大多数开发人员在创建线程时所具有的功能。它们也更容易优雅地取消,甚至可以报告进度。
答案 5 :(得分:0)
尝试使用BackgrounWorker并在其RunWorkerCompleted事件中注册处理程序。
答案 6 :(得分:0)
在Winforms中,您可以使用.Invoke方法(并检查.InvokeRequired属性)来将回调编组回UI线程。您没有那么多通知UI线程 - 它继续进行并且不等待任何类型的完成,但您可以使用另一个线程与控件交互(例如,更新标签的text属性) Invoke方法。
您还可以使用BackgroundWorker对象(阅读MSDN以了解有关它的更多信息),该对象实现了一个回调功能,可以在后台工作完成后在UI线程上运行一些代码。
答案 7 :(得分:0)
您可以使用Dispatcher.CurrentDispatcher存储对UI线程Dispatcher的引用(显然在GUI线程调用的方法中)。使用此对象,您可以在工作线程中使用BeginInvoke或Invoke方法在GUI线程上执行一个方法,通知它您已完成工作。我个人认为这种方法比使用后台工作者对象稍微灵活一些,并且可以产生稍微更易读的代码。
答案 8 :(得分:0)
在C#中使用多个线程有一种简单的方法。它被称为BackgroundWorker。 你应该看一下:BackgroundWorker Tutorial
答案 9 :(得分:0)
如前所述,可以使用BackgroundWorker类。
或者,你可以做类似于以下的事情:
void buttonGo_Clicked( object sender, EventArgs e )
{
MyAsyncClass class = new MyAsyncClass();
class.LongOperationFinished += (LongOperationFinishedEventHandler)finished;
class.BeginLongOperation();
}
void finished( object sender, EventArgs e )
{
if( this.InvokeRequired ) {
this.BeginInvoke( (LongOperationFinishedEventHandler)finished, sender, e );
return;
}
// You can safely modify the gui here.
}