BackgroundWorker.CancellationPending是如何线程安全的?

Tom*_*Tom 22 .net c# multithreading backgroundworker thread-safety

取消BackgroundWorker操作的方法是调用BackgroundWorker.CancelAsync():

// RUNNING IN UI THREAD
private void cancelButton_Click(object sender, EventArgs e)
{
    backgroundWorker.CancelAsync();
}
Run Code Online (Sandbox Code Playgroud)

在BackgroundWorker.DoWork事件处理程序中,我们检查BackgroundWorker.CancellationPending:

// RUNNING IN WORKER THREAD
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    while (!backgroundWorker.CancellationPending) {
        DoSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

上述想法遍布整个网络,包括在BackgroundWorker的MSDN页面上.

现在,我的问题是:这个线程安全怎么样?

我已经查看了ILSpy中的BackgroundWorker类 - CancelAsync()只需将cancellationPending设置为true而不使用内存屏障,CancellationPending只需返回cancellationPending而不使用内存屏障.

根据这个Jon Skeet页面,上面的内容不是线程安全的.但是BackgroundWorker.CancellationPending的文档说:"这个属性是供工作线程使用的,它应该定期检查CancellationPending并在设置为true时中止后台操作."

这里发生了什么?它是否是线程安全的?

Han*_*ant 12

这是因为BackgroundWorker继承自继承自MarshalByRefObject的Component.MBRO对象可以驻留在另一台机器上,也可以驻留在另一个进程或应用程序域中.这可以通过让代理模拟具有所有完全相同的属性和方法但其实现通过网络封送调用的方式来工作.

其中一个副作用是抖动无法内联方法和属性,这会破坏代理.这也阻止了将字段值存储在cpu寄存器中的任何优化.就像挥发性一样.

  • @Dmitry - 我在答案中解释道.很抱歉,我无法解释得很清楚,*volatile*的主题是相当难以理解的,直到您了解机器代码以及为什么CPU寄存器很重要. (2认同)

Hen*_*man 8

它是线程安全的.
代码

  while (!backgroundWorker.CancellationPending) 
Run Code Online (Sandbox Code Playgroud)

正在读取属性,编译器知道它无法缓存结果.

因为在正常框架中,每个Write都与VolatileWrite相同,所以CancelAsync()方法只能设置一个字段.