Backgroundworker的RunWorkerCompleted事件在完成工作之前就被触发了。

3
我正在使用BackgroundWorker线程执行长时间任务(基本上是读取一个大型XML文件)。第一次工作正常,但如果我上传第二个XML文件并使用相同的BackgroundWorker,有时它能正常工作,但大多数情况下在DoWork事件之前就会触发Backgroundworker的RunWorkerCompleted。以下是部分代码:
    private void openFile_Click(object sender, RoutedEventArgs e)
    {
          // Code removed for brevity
  worker = new BackgroundWorker();
            worker.RunWorkerAsync();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.WorkerReportsProgress = true;
            worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
       }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        DataImport();
        //worker.Dispose();
        //worker.Disposed += new EventHandler(worker_Disposed);
        //worker.DoWork -= worker_DoWork;
        //worker.RunWorkerCompleted -= worker_RunWorkerCompleted;
        //worker = null;
        //GC.Collect(GC.GetGeneration(worker), GCCollectionMode.Forced);
    }

worker是一个全局定义的变量。我不明白这里有什么问题。请帮忙解释一下。


一些针对已完成的提示:不要调用 GC.Collect(),要检查 e.Error - H H
1
不看到 DoWork() 的概要,无法回答该问题。 - H H
如果你的DoWork函数生成一个线程来执行它的工作,而你没有使用.Join()方法,那么DoWork将立即返回。但正如@Henk所说,需要看一下DoWork函数具体做了什么。 - bland
5个回答

9

在调用 RunWorkerAsync() 前,应该添加 DoWork 事件处理程序(以及所有其他事件处理程序)

否则,可能会发生 RunWorkerAsync 几乎不执行任何操作的情况。


1
是真的,但并没有真正解释“在DoWork事件之前被触发”的症状。 - H H
可能还需要提醒一下,由于他正在重复使用工作器,所以他应该确保不要多次添加事件处理程序。 - Jim Mischel
@JimMischel 除此之外,他正在重复使用事件处理程序,因此可能被触发的 RunWorkerCompleted 事件不属于“当前”的后台工作器。 - sloth
OP 每次都在创建一个新的 Bgw。而已注释掉的代码确实试图清除旧的 Bgw。两者都足以排除处理程序的重用。 - H H
@DominicKexel 非常感谢您的回复。但是为什么会这样,RunWorkerCompleted 事件应该在工作完成后才执行,无论我们将处理程序分配到事件的哪个位置。有任何想法吗? - Vikram
1
@Vikram,RunWorkerCompleted += ... 这部分代码并不是由您调用的,而是由 BackgroundWorker 调用的。您只是为其分配了工作,等待它在适当的时间执行。更重要的是,在同一概念上,您需要在 RunWorkerAsync() 前分配 DoWork+=,因为 BGW 需要在确定处理某个事件的时间时,有一个分配的函数。RunWorkerAsync 将启动,并在确定资源足够的情况下开始分配的 "DoWork"。当 "DoWork" 完成后,BGW 将知道并检查是否已经分配 "Completed"。 - bland

2
应该是这样的:
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;

worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += 
    new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += 
    new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

worker.RunWorkerAsync();

RunWorkerAsync 应该在订阅 DoWorkRunWorkerCompleted 事件之后调用。


2

首先,您需要检查后台工作程序是否正在运行,使用以下方法...

        backgroundWorker1.DoWork += backgroundWorker1_DoWork;
        backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
        if (backgroundWorker1.IsBusy)
        {
            backgroundWorker1.CancelAsync();
        }
        else
        {
            backgroundWorker1.RunWorkerAsync();

        }

0
如果您在BackgroundWorker的_DoWork事件中调用另一个异步函数,例如:
    private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        somethingsToDoAsync();
        // somethingsToDoAsync() function is to ASYNC 
    }

_RunWorkerCompleted在_Dowork事件完成之前就会触发。

将另一个函数更改为非异步函数。


0
https://dev59.com/H3LYa4cB1Zd3GeqPaatm#16809596所述,当DoWork方法由于异常而终止时,您可能会盲目地假设该工作者已完成作业并产生了结果。这个异常被BackgroundWorker捕获,并作为e.Error属性传递给RunWorkerCompleted事件处理程序。
因此,我建议您使用try/catch语句检查那个属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接