【发布时间】:2010-10-30 16:35:46
【问题描述】:
我需要让RunWorkerAsync() 返回一个List<FileInfo>。
能够从后台工作人员返回对象的过程是什么?
【问题讨论】:
标签: c# concurrency asynchronous backgroundworker worker-process
我需要让RunWorkerAsync() 返回一个List<FileInfo>。
能够从后台工作人员返回对象的过程是什么?
【问题讨论】:
标签: c# concurrency asynchronous backgroundworker worker-process
在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());
}
【讨论】:
我假设您不想阻塞并等待 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...
}
【讨论】:
RunWorkerAsync() 异步启动进程,并在进程实际完成之前返回并继续执行您的代码。如果您想获得BackgroundWorker 的结果,您需要创建一个实例变量来保存该值,并在BackgroundWorker 完成后检查它。
如果您想等到工作完成,则不需要BackgroundWorker。
【讨论】:
BackgroundWorker 这样的旧组件的上下文中,“等待”的语义相对复杂(或者至少要演示它是如何“等待”的) )。现在使用async 和await 语法更容易表达类似的东西,并且允许开发人员完全取消BackgroundWorker 的大多数用例。
为了补充 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;。实际的元组妨碍了。只需要它的值。
根据您的模型,您要么希望工作线程在其完成工作时回调其创建者(或其他进程),要么您必须经常轮询工作线程以查看它是否已完成如果是,则得到结果。
等待工作线程返回其结果的想法破坏了多线程的好处。
【讨论】:
你可以让你的线程以对象作为参数引发一个事件:
ThreadFinishedEvent(this, new ThreadEventArgs(object));
地点:
public class ThreadEventArgs : EventArgs
{
public ThreadEventArgs(object object)
{
Object = object
}
public object Object
{
get; private set;
}
}
【讨论】:
一般来说,当运行异步进程时,工作线程应该调用委托或触发事件(如 ChrisF)。
您可以查看新的 PFX,它具有一些可以返回值的并发函数。
例如,有一个名为 Parallel.ForEach() 的函数,它有一个可以返回值的重载。
查看这里了解更多信息
【讨论】:
不要在“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();
}
【讨论】: