【问题标题】:Passing object from backgroundworker to main thread将对象从后台工作者传递到主线程
【发布时间】:2011-07-22 19:29:35
【问题描述】:

我有一个 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
    }

所以,如果有打印到 stderr 的内容,我可以在调试器中看到它,但如果我尝试对字符串 stderr 执行任何操作,例如我有一个名为“_tbLog”的文本框并且做了

_tbLog.Text+=stderr;    

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

【问题讨论】:

    标签: c# .net multithreading backgroundworker


    【解决方案1】:

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

    【讨论】:

    • 谢谢!效果很好。刚刚添加了 e.Result = stderr;然后在 workercompleted 中我能够使用 _tbLog.Text+=e.Result 作为字符串;
    • 请注意,读取 e.Result 将显示在 DoWork 中未处理的任何异常。完成的事件应该遵循一个模式,见msdn.microsoft.com/en-us/library/…
    【解决方案2】:

    使用您的 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;
       }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        您也可以像@Zembi 提到的那样使用调度程序:

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

        您还可以使用TPL 确保事情在正确的线程上运行

        -编辑-

        这里是a good article,介绍了进行 ui 更新的不同方法,包括使用 TPL

        【讨论】:

        • 我已经按照 CrazyDart 的解决方案让它工作了,但是如果我使用 TPL,并且让进程在正确的线程(主线程)上运行,那也不会阻止 GUI重绘,锁定应用程序?
        • 如果你在 gui 线程上运行整个过程,是的。然而,tpl 的主要优点之一是它可以轻松地在使用延续(ContinueWith(..) 方法)和调度程序的线程之间传递工作。您将在线程池上启动一个新任务,完成后您将在更新 ui 的 ui 线程上安排一个新任务。
        猜你喜欢
        • 1970-01-01
        • 2017-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-30
        • 1970-01-01
        • 2013-06-05
        相关资源
        最近更新 更多