C# 将对象从backgroundworker传递到主线程
我有一个WPF应用程序,它执行外部程序来处理媒体文件,为了在处理媒体文件时GUI不会冻结,我通过backgroundworker在单独的线程上执行该过程C# 将对象从backgroundworker传递到主线程,c#,.net,multithreading,backgroundworker,C#,.net,Multithreading,Backgroundworker,我有一个WPF应用程序,它执行外部程序来处理媒体文件,为了在处理媒体文件时GUI不会冻结,我通过backgroundworker在单独的线程上执行该过程 private void BackgroundWorkerExecProcess(Process process) { BackgroundWorker worker = new BackgroundWorker(); worker.WorkerReportsProgress = fals
private void BackgroundWorkerExecProcess(Process process)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = false;
worker.DoWork += DoWork;
worker.RunWorkerCompleted += WorkerCompleted;
worker.RunWorkerAsync(process);
}
void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Process process = e.Argument as Process;
process.Start();
string stderr = process.StandardError.ReadToEnd();
//I want to display stderr on main thread
process.WaitForExit();
}
void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//some code to update gui telling user that process has finished
}
因此,如果有打印到stderr的内容,我可以在调试器中看到它,但是如果我尝试使用字符串stderr执行任何操作,例如,如果我有一个名为“\u tbLog”的文本框,并且
我从编译器那里得到一个关于它们在不同线程上的错误。是否有方法将对象从工作线程传递到主线程?在DoWork中,将e.Result设置为对象。在WorkerCompleted中,您可以将该对象取回。。。它将再次成为e.object类型的结果。把它扔到原来的物体上。完成的工件应在正确的螺纹上 这是我的一个:
private void workerUpdateBuildHistory_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
UpdateStatusModel model = (UpdateStatusModel)e.Argument;
BuildService buildService = new BuildService(model.TFSUrl);
e.Result = buildService.UpdateBuildHistoryList(model);
}
private void workerUpdateBuildHistory_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
BuildHistoryListModel model = (BuildHistoryListModel)e.Result;
if (model != null)
{
listViewPastBuilds.Items.Clear();
foreach (var item in model.Builds)
{
listViewPastBuilds.Items.Add(item);
}
}
}
首先,您需要将任何结果对象(在本例中为字符串列表)放入DoWorkEventArgs.result属性中,然后通过RunWorkerCompletedArgs.result属性检索该对象 然后,连接后台工作程序的事件处理程序RunWorkedCompleted事件,并让它传回RunWorkerCompletedEventArgs.Result属性中需要的任何对象 例如:
void DoWork(object sender, DoWorkEventArgs arg)
{
List<string> results = new List<string>();
results.Add("one");
results.Add("two");
results.Add("three");
arg.Results = results;
}
void WorkComplete(object sender, runWorkerCompelteEventArgs arg)
{
//Get our result back as a list of strings
List<string> results = (List<string>)arg.Result;
PrintResults(results);
}
void DoWork(对象发送方,DoWorkEventArgs参数)
{
列表结果=新列表();
结果。添加(“一”);
结果。添加(“两”);
结果。添加(“三”);
arg.Results=结果;
}
void WorkComplete(对象发送方,runworkercompleteeventargs参数)
{
//将结果作为字符串列表返回
列表结果=(列表)arg.Result;
打印结果(结果);
}
注意:我还没有测试过这段代码,但我相信它应该可以编译。
您也可以使用调度器,因为@Zembi提到:
this.Dispatcher.Invoke( new Action( () => {
_tbLog.Text+=stderr;
} ) );
您还可以使用来确保在正确的线程上运行
-编辑-
这里介绍了进行ui更新的不同方法,包括使用TPL,使用WorkerCompleted事件处理程序对ui进行更改,它在正确的线程上运行。您所要做的就是将字符串传递给事件处理程序。这就是DowereVentargs设计的结果。您将在事件处理程序中从e.Result中检索它。因此:
void DoWork(object sender, DoWorkEventArgs e)
{
//...
e.Result = stderr;
}
void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null) DisplayError(e.Error);
else _tbLog.Text += (string)e.Result;
}
谢谢这很有效。刚刚添加了e.Result=stderr;然后在workercompleted中,我能够使用_tbLog.Text+=e.Result作为字符串;请注意,读取e.Result将显示DoWork中未处理的任何异常。完成的事件应该遵循一种模式,请看,我已经按照CrazyDart的解决方案让它工作了,但是如果我使用TPL,并且让进程在正确的线程(主线程)上运行,这不也会阻止GUI重新绘制,锁定应用程序吗?如果在GUI线程上运行整个进程,是的。然而,tpl的一个关键好处是,它很容易在使用continuations(ContinueWith(..)方法)的线程和调度器之间传递工作。您将在线程池上启动一个新任务,完成后,您将在更新ui的ui线程上安排一个新任务。
void DoWork(object sender, DoWorkEventArgs e)
{
//...
e.Result = stderr;
}
void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null) DisplayError(e.Error);
else _tbLog.Text += (string)e.Result;
}