2012-05-17 99 views
2

我有一个后台工作人员,需要基于复选框的数量被调用多次 - 我写了这个来获取复选框的值,并将它们放入ListRunWorkerAsync关闭完成的BackgroundWorker

List repSelected = new List();

这是填充,然后遍历像这样:

foreach (string rep in repSelected) 
{ 
    backgroundWorker1.RunWorkerAsync(rep); 
    backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); 
}

异步,DoWork的代码如下所示:

BackgroundWorker worker = sender as BackgroundWorker; 
string rep = e.Argument.ToString(); 

if (worker.CancellationPending == true) 
{ 
    e.Cancel = true; 
} 
else 
{ 
    DirectoryExists(rep); 
    ProcessRunner(rep); //Rars some large files - expensive 
}

过程然后运行WorkerComplete,问题是当该过程返回去执行Worker的下一次迭代,它崩溃时表示该worker正忙 - 即使worker已经返回它的WorkerCompleted状态。

如何确保在循环的下一次迭代之前关闭线程?

注:我有一个包含!backgroundWorker1.IsBusy()的背景工作条件,但是这(显然)只是跳过剩余的迭代而不执行。

回答

1

如果要按顺序处理每个项目,没有理由为每个任务使用单独的背景工作。所以将foreach循环移动到DoWork方法会更好。但是,如果您想要以并列方式处理所有项目,则需要为每个项目创建一个后台工作人员。

+0

非常好,正是我之后,谢谢!出于好奇,是否有可能为每个项目创建一个新的后台工作者,但仍然使用相同的代码来执行? –

+1

您可以将DoWork代码移动到单独的方法中,并使用rep值作为参数。但是,在您的代码中,似乎您正在处理文件,因此如果将它们放置在机械驱动器(而非SSD)上,那么如果按顺序处理文件,我认为您的代码的性能会更好。这是因为您将避免来回移动磁盘头。 –

+0

@HenkHolterman使用1 Backgroundworker with parallel.foreach将导致相同的异常。 – daryal

1

从MSDN:

如果后台操作已经运行,再次调用的RunWorkerAsync会引发一个InvalidOperationException。

所以你不能使用BackgroundWorker维护任务的队列(和你推所有任务的顺序,而不用等待一个之前完成)。您有此不同的解决方案,例如,如果你想使用BackgroundWorker你可以这样做是为了保持:

backgroundWorker1.RunWorkerAsync(repSelected); 

然后改变你的DoWork方法类似如下:

BackgroundWorker worker = sender as BackgroundWorker; 

foreach (string rep in (IEnumerable<string>)e.Argument) 
{ 
    if (worker.CancellationPending == true) 
    { 
     e.Cancel = true; 
     return; 
    } 
    else 
    { 
     DirectoryExists(rep); 
     ProcessRunner(rep); //Rars some large files - expensive 
    } 
} 

作为替代你可以考虑更改执行此任务的方式,例如使用System.Threading.Tasks.TaskThreadPool(直接或间接地将大多数并行操作排入池中)。

1

您的foreach代码将立即触发所有元素的工作。这就是为什么你会得到例外。

如果要按顺序启动工作程序,则只能在启动时调用RunWorkerAsync一次,然后再为每个WorkerComplete事件调用RunWorkerAsync。但为什么不在工人代码中进行处理呢?

1

BackgroundWorker真的很忙,因为当第一次调用backgroundWorker1.RunWorkerAsync(rep);时,它不会等待任何事情,那么将立即调用第二,第三,...调用。

您应该为每次调用创建BackgroundWorker,然后就可以了。

1

你可以使用parallel.foreach和多个backgroundworkers;

Parallel.ForEach(YourListofStrings, 
       (q) => 
       { 
        BackgroundWorker worker = new BackgroundWorker(); 
        worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
        worker.RunWorkerAsync(q); 
       }); 
+0

良好的解决方案 - 但我会采取Espen的建议,并按顺序运行以避免抖动硬盘。 +1 –