将对象从backgroundworker传递到主线程

时间:2011-07-22 19:29:36

标签: c# .net multithreading backgroundworker

我有一个执行外部程序来处理媒体文件的WPF应用程序,因此在处理媒体文件时GUI不会冻结,我通过后台工作人员在一个单独的线程上执行该过程。

        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做任何事情,例如我有一个名为“_tbLog”的文本框并且

_tbLog.Text+=stderr;    

我从编译器得到一个关于它们在不同线程上的错误。有没有办法将对象从工作线程传递给主线程?

4 个答案:

答案 0 :(得分:8)

在DoWork中,将e.Result设置为您的对象。在WorkerCompleted中,您可以将该对象取回...它将再次成为类型对象的e.Result。只需将它投射到它的对象上即可。 WorkerCompleted应该在正确的帖子上。

这是我的一个:

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);
        }
    }
}

答案 1 :(得分:3)

使用您的WorkerCompleted事件处理程序对UI进行更改,它在正确的线程上运行。您所要做的就是将字符串传递给事件处理程序。这是DoWorkEventArgs.Result的目的。您将从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;
   }

答案 2 :(得分:2)

首先,您需要在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);
}

注意:我没有测试过这段代码,但我相信它应该编译。 http://msdn.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.result.aspx http://msdn.microsoft.com/en-us/library/system.componentmodel.doworkeventargs.aspx

答案 3 :(得分:0)

您也可以使用调度程序@Zembi提及:

  this.Dispatcher.Invoke( new Action( () => {
    _tbLog.Text+=stderr; 
  } ) );

你也可以使用TPL来确保在正确的线程上运行

-edit -

以下是关于进行ui更新的不同方法的a good article,包括使用TPL