简体   繁体   English

将 object 从 backgroundworker 传递到主线程

[英]Passing object from backgroundworker to main thread

I have a WPF application that executes external programs to process media files, and so that the GUI doesn't freeze when the media files are being processed, I execute the process on a separate thread through backgroundworker.我有一个 WPF 应用程序,它执行外部程序来处理媒体文件,因此在处理媒体文件时 GUI 不会冻结,我通过 backgroundworker 在单独的线程上执行该进程。

        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
    }

so, if there is something printed to stderr, I can see it in the debugger, but if I try to do anything with the string stderr, such as if I have a textbox called "_tbLog" and did所以,如果有东西打印到stderr,我可以在调试器中看到它,但是如果我尝试对字符串stderr做任何事情,比如我有一个名为“_tbLog”的文本框并且做了

_tbLog.Text+=stderr;    

I get an error from the compiler about them being on separate threads.我从编译器那里得到一个错误,关于它们在不同的线程上。 is there a way to pass the object from the worker thread to the main thread?有没有办法将 object 从工作线程传递到主线程?

In DoWork, set e.Result to your object.在 DoWork 中,将 e.Result 设置为 object。 In the WorkerCompleted you can get that object back out... it will once again be e.Result of type object.在 WorkerCompleted 中,您可以将 object 退出...它将再次成为 object 类型的 e.Result。 Just cast it to the object it was.只需将其转换为 object 即可。 The WorkerCompleted should be on the correct thread. WorkerCompleted 应该在正确的线程上。

Here is one of mine:这是我的一个:

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

Use your WorkerCompleted event handler to make changes the UI, it runs on the right thread.使用您的 WorkerCompleted 事件处理程序来更改 UI,它在正确的线程上运行。 All you have to do is pass the string to the event handler.您所要做的就是将字符串传递给事件处理程序。 Which is what DoWorkEventArgs.Result was designed to do.这就是 DoWorkEventArgs.Result 的设计目的。 You'll retrieve it in the event handler from e.Result.您将在 e.Result 的事件处理程序中检索它。 Thus:因此:

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

First you need to place whatever result object (in this example, a list of strings) in the DoWorkEventArgs.Result property, then retrieve this object via the RunWorkerCompletedArgs.Result property首先,您需要在 DoWorkEventArgs.Result 属性中放置任何结果 object(在此示例中为字符串列表),然后通过 RunWorkerCompletedArgs.Result 属性检索此 object

Then, hook up an event handler RunWorkedCompleted event of the Background worker and have it pass back whatever object you want in the RunWorkerCompletedEventArgs.Result property.然后,连接后台工作程序的事件处理程序 RunWorkedCompleted 事件,并让它在 RunWorkerCompletedEventArgs.Result 属性中传回您想要的任何 object。

Example:例子:

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

Note: I have not tested this code, but I believe it should compile.注意:我没有测试过这段代码,但我相信它应该可以编译。 http://msdn.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.result.aspx http://msdn.microsoft.com/en-us/library/system.componentmodel.doworkeventargs.aspx http://msdn.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.result.aspx http://msdn.microsoft.com/en-us/library/system.componentmodel.doworkeventargs.aspx

you can also use the dispatcher as @Zembi mentiones:您还可以像@Zembi 提到的那样使用调度程序:

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

you can also use TPL to make sure things get run on the right thread您还可以使用TPL确保事情在正确的线程上运行

-edit- -编辑-

Here is a good article on diffrent ways to do ui updates, including using TPL这是一篇关于进行 ui 更新的不同方法的好文章,包括使用 TPL

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM