WPF BackgroundWorker.RunWorkerAsync锁

时间:2014-11-18 09:04:52

标签: c# wpf multithreading backgroundworker sync

我想在WPF中使用BackgroundWorker实例来执行一些计算绑定操作。可以在不同位置的一个精确实例上调用RunWorkerAsync(...)方法。如果在IsBusy为true时调用它,则抛出InvalidOperationException。有人可以在if(...)块中的检查和以下代码中相应的RunWorkerAsync(1)调用之间运行worker吗?

    if(!_bgWorker.IsBusy) {
        _bgWorker.RunWorkerAsync(1);
    }

如果发生了抛出InvalidOperationException?在调用RunWorkerAsync(...)时,我是否应该像下面这样写smth?

lock(_bgWorker) {
    if(!_bgWorker.IsBusy) {
        _bgWorker.RunWorkerAsync(1);
    }
}

锁定关键字是一种很好的技术来实现我想要的吗?

1 个答案:

答案 0 :(得分:0)

假设此代码在UI线程上执行,例如在KeyUp事件处理程序中:

private void someControl_KeyUp(object sender, KeyEventArgs e) {
    if(!_bgWorker.IsBusy) {
        _bgWorker.RunWorkerAsync(1);
    }
}

...并假设您只是从UI线程启动工作程序,然后我们可以确保IsBusy属性在检查时间和对RunWorkerAsync()的调用之间不会发生变化。

详细阐述这一点......

您希望避免两种有趣的方案:

  1. IsBusy是假的,但在你打电话之前就变为真 的RunWorkerAsync()。
  2. IsBusy是真的,但之前变为假 此方法返回
  3. 第一种情况可能更危险,因为如果工作人员已经开始,你当然无法启动它。但是你知道第一种情况不可能发生,因为a)你只是从UI线程启动工作者,在此事件处理程序返回之前,没有其他任何东西可以在UI线程上执行,并且b)假设你正在遵循显示的模式{ {3}},启动worker的唯一其他位置是RunWorkerCompleted事件处理程序,当然,如果worker没有运行(即IsBusy为false),则无法执行。< / p>

    第二种情况本身并不危险(即没有例外),但当然可能导致工人在你想要的时候没有被运行。但是您知道这种情况也不会发生,因为工作人员只能在IsBusy事件处理程序完成后从!IsBusy转换为RunWorkerCompleted。由于该事件处理程序在UI线程上执行,就像这个事件处理程序一样,您知道它在此事件处理程序运行时没有运行,并且在此事件处理程序返回之前无法开始运行。

    换句话说,需要执行所有这些代码的UI线程充当方法之间的必要同步。单个线程不能与自己竞争;在该线程上发生的一切都需要一次发生一件事。即一旦一个方法开始在该线程上执行,在第一个方法返回之前,没有其他方法可以在同一个线程上执行(当然,第一个方法调用该方法,但这里没有问题)。

    警告:在C#中,请务必记住,对于该方法的单次调用,会多次执行某些类型的方法。即他们在完成之前返回。但是,这些很容易注意到:迭代器方法甚至使用return关键字(即在yield return中)来表示这一点。发生这种情况的另一个地方是async方法:在任何await语句中,该方法可以返回(并且通常会),允许其他方法在UI线程上执行。

    另一个例外是,如果在当前运行的方法返回之前,调用Application.DoEvents()(即直接由方法调用,或者通过它调用的某个方法间接调用),这可以允许处理其他窗口消息因此要调用一些其他事件处理程序代码。这种行为,因为它打破了UI线程执行的正常契约,这就是为什么我建议使用永远不会的方法(它从来没有真正需要...它只是用来作为一个黑客来绕过其他一些设计代码中的问题)。

    这些例外都不适用于此,但我想确保提及它们,以便答案中的一般事实陈述不会被误解和过于宽泛地应用。