【问题标题】:RunWorkerAsync close BackgroundWorker on completeRunWorkerAsync 在完成时关闭 BackgroundWorker
【发布时间】:2012-05-17 10:34:34
【问题描述】:

我有一个后台工作人员需要根据选中的复选框数量多次调用 - 我编写了这个来获取复选框值并将它们放入 List。

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() 的后台工作人员有一个条件,但这(显然)只是跳过了剩余的迭代而没有执行。

【问题讨论】:

    标签: c# .net multithreading asynchronous backgroundworker


    【解决方案1】:

    如果您想按顺序处理每个项目,则没有理由为每个任务使用单独的后台工作程序。所以最好将 foreach 循环移到 DoWork 方法中。但是如果你想并行处理所有的item,你需要为每个item创建一个后台worker。

    【讨论】:

    • 太好了,正是我所追求的,谢谢!只是出于好奇,是否可以为每个项目创建一个新的后台工作人员,但仍使用相同的代码来执行?
    • 您可以将 DoWork 代码移动到单独的方法中,并使用 rep 值作为参数。但是,在您的代码中,您似乎正在处理文件,因此如果将它们放在机械驱动器(而不是 SSD)上,我认为如果您按顺序处理文件,您的代码会执行得更好。那是因为您将避免来回移动磁盘头。
    • @HenkHolterman 使用 1 个 Backgroundworker 和 parallel.foreach 将导致相同的异常。
    【解决方案2】:

    您的 foreach 代码将立即触发所有元素的工作。这就是你得到异常的原因。

    如果你想按顺序启动 Worker,你只能在启动时调用 RunWorkerAsync 一次,然后为每个 WorkerComplete-event 调用。但是为什么不在工作代码中进行处理呢?

    【讨论】:

      【解决方案3】:

      BackgroundWorker 真的很忙,因为当第一次调用backgroundWorker1.RunWorkerAsync(rep); 时,它没有等待任何东西,然后立即调用第二个、第三个...。

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

      【讨论】:

        【解决方案4】:

        来自 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.Task 或ThreadPool(直接或间接地,大多数并行操作将排队到池中)。

        【讨论】:

          【解决方案5】:

          您可以使用 parallel.foreach 和多个后台工作人员;

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

          【讨论】:

          • 很好的解决方案 - 但我会听从 Espen 的建议并按顺序运行以避免损坏硬盘。 +1
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多