【问题标题】:How to make BackgroundWorker return an object如何让 BackgroundWorker 返回一个对象
【发布时间】:2010-10-30 16:35:46
【问题描述】:

我需要让RunWorkerAsync() 返回一个List<FileInfo>

能够从后台工作人员返回对象的过程是什么?

【问题讨论】:

    标签: c# concurrency asynchronous backgroundworker worker-process


    【解决方案1】:

    BackgroundWorker(后台工作发生的地方)的DoWork 事件处理程序中有一个参数DoWorkEventArgs。此对象具有公共属性对象 Result。当您的工作人员生成其结果(在您的情况下为List<FileInfo>)时,将e.Result 设置为该结果,然后返回。

    现在您的 BackgroundWorker 已完成其任务,它会触发 RunWorkerCompleted 事件,该事件有一个 RunWorkerCompletedEventArgs 对象作为参数。 RunWorkerCompletedEventArgs.Result 将包含来自您的 BackgroundWorker 的结果。

    示例:

    private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        int result = 2+2;
        e.Result = result;
    }
    
    private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        int result = (int)e.Result;
        MessageBox.Show("Result received: " + result.ToString());
    }
    

    【讨论】:

      【解决方案2】:

      我假设您不想阻塞并等待 RunWorkerAsync() 获得结果(如果您这样做了,就没有理由运行异步!

      如果您想在后台进程完成时收到通知,请挂钩 RunWorkerCompleted 事件。如果你想返回一些状态,在 DoWork 的事件 args 的 Result 成员中返回。

      例子:

      private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // do your thing .... // return results e.Result = theResultObject; } // now get your results private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MyResultObject result = (MyResultObject)e.Result; // process your result... }

      【讨论】:

      • BackgroundWorker_DoWork方法中不需要设置e.Result。将返回类型设置为您希望它返回的类型,API 将为您设置 e.Result。这就是 API 的工作原理,而这个 11 岁以上的帖子中的每个答案都忽略了这一点。
      • 你在想别的课吗?在 .Net 5 文档中,System.ComponentModel.Background 工作人员为 DoWork 方法使用了一个 void 委托。 .Net 5 的文档指示使用 .Result 成员。 docs.microsoft.com/en-us/dotnet/api/…docs.microsoft.com/en-us/dotnet/api/…
      【解决方案3】:

      RunWorkerAsync() 异步启动进程,并在进程实际完成之前返回并继续执行您的代码。如果您想获得BackgroundWorker 的结果,您需要创建一个实例变量来保存该值,并在BackgroundWorker 完成后检查它。

      如果您想等到工作完成,则不需要BackgroundWorker

      【讨论】:

      • 等待和锁定用户界面是有区别的。
      • @TSar:的确如此,尽管在使用像BackgroundWorker 这样的旧组件的上下文中,“等待”的语义相对复杂(或者至少要演示它是如何“等待”的) )。现在使用asyncawait 语法更容易表达类似的东西,并且允许开发人员完全取消BackgroundWorker 的大多数用例。
      • 那么用替代方案来补充你的答案会很好,而不是仅仅告诉 OP 不要使用 BackgroundWorker。现在看来,您似乎是在建议不要使用任何类型的多线程——这最终会锁定应用程序,直到进程完成。
      【解决方案4】:

      为了补充 David 的回答,可能需要推送一个元组以向方法提供多个参数。

      为此,让我更新他的答案,其中一个值(称为engagementId)通过每个调用传递,并且元组保存该原始项目以供使用以及结果。 p>

      private void bgw_DoWork(object sender, DoWorkEventArgs e)
      {
          var engagementId = (int)e.Argument;
          int result = 2 + 2;
          e.Result = (engagementId, result);
      }
      
      private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
          (int engagementId, int result) tupleResult = ((int, int)) e.Result; // Both (( are needed for tuple/casting.
      
          MessageBox.Show($"Result received {tupleResult.result} for engagement {tupleResult.engagementId}");
      }
      

      更多信息请查看How To Cast To A Tuple的回复。

      【讨论】:

      • 我更喜欢像这样直接解构元组:var (engagementId, result) = ((int, int))e;。实际的元组妨碍了。只需要它的值。
      【解决方案5】:

      根据您的模型,您要么希望工作线程在其完成工作时回调其创建者(或其他进程),要么您必须经常轮询工作线程以查看它是否已完成如果是,则得到结果。

      等待工作线程返回其结果的想法破坏了多线程的好处。

      【讨论】:

        【解决方案6】:

        你可以让你的线程以对象作为参数引发一个事件:

        ThreadFinishedEvent(this, new ThreadEventArgs(object));
        

        地点:

        public class ThreadEventArgs : EventArgs
        {
            public ThreadEventArgs(object object)
            {
                Object = object
            }
        
            public object Object
            {
                get; private set;
            }
        }
        

        【讨论】:

          【解决方案7】:

          一般来说,当运行异步进程时,工作线程应该调用委托或触发事件(如 ChrisF)。

          您可以查看新的 PFX,它具有一些可以返回值的并发函数。

          例如,有一个名为 Parallel.ForEach() 的函数,它有一个可以返回值的重载。

          查看这里了解更多信息

          http://msdn.microsoft.com/en-us/magazine/cc817396.aspx

          【讨论】:

            【解决方案8】:

            不要在“DoWork”方法中进行后台工作,而是创建一个返回您想要返回的类型的方法,并将其应用于 e.Result,就像这里推荐的其他答案一样。作为一个最小的例子,它回答了 OP 的问题,而不会使事情过于复杂......

                    private List<FileInfo> FileInfoWorker(object sender, DoWorkEventArgs e)
                {
                    return new List<FileInfo>(new DirectoryInfo("C:\\SOTest").GetFiles().ToList());
                }
            
                private void bgwTest_DoWork(object sender, DoWorkEventArgs e)
                {
                    BackgroundWorker worker = sender as BackgroundWorker;
            
                    e.Result = FileInfoWorker(worker, e);
                }
            
                private void bgwTest_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
                {
                    if (e.Error != null)
                    {
                        MessageBox.Show(e.Error.Message);
                    }
                    else if (e.Cancelled)
                    {
                        tbStatus.Text = "Background operation cancelled.";
                    }
                    else
                    {
                        tbStatus.Text = "Background operation complete.";
                    }
                }
            

            该示例还展示了如何从 BackgroundWorker API 更新 TextBox。未显示对通过 ProgressBar 和 TextBoxes 报告进度的支持以及对取消的支持,API 也支持这两者。代码通过按钮运行...

                    private void btnSOTest_Click(object sender, EventArgs e)
                {
                    bgwTest.RunWorkerAsync();
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-09-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多