在C#中线程和UI之间共享变量

4
我能安全地在这两个线程之间共享这个“状态”对象吗?
    private bool status = false;

    private void uiNewThread_bootloaderStartIdSetupAuto()
    {
        while (status)
            ;
    }

以下是将从下面的用户界面启动的新线程:
    private void uiBtnBootloaderStartIdSetupAuto_Click(object sender, EventArgs e)
    {
        if (MessageBox.Show("ID will be setup starting from 1 to 16. \n\nAfter pressing 'YES', press the orange button one-by-one on the nodes.\nThe first pressed node will have number 1, the next number 2, and so on... \n\nWhen done, hit DONE button.", "ID setup", MessageBoxButtons.YesNo) == DialogResult.Yes)
        {
            status = true;
            Thread transmitConfig = new Thread(new ThreadStart(uiNewThread_bootloaderStartIdSetupAuto)); //close port in new thread to avoid 
            transmitConfig.Start();
        }
        else
        {
            Log(LogMsgType.Normal, "User cancelled");
            status = false;            
        }
    }
2个回答

4

编译器或CPU进行的缓存或重排序等优化可能会破坏您的代码。为了防止这种情况发生,您应该声明字段volatile:

private volatile bool status = false;

可能出现的问题是,如果两个线程在不同的核心上运行,则在轮询线程正在运行的核心中,status的值可能会被CPU寄存器缓存,从而永远无法看到另一个线程更新的值。

尝试以发布模式构建您的应用程序,您应该会看到这种效果。


在大多数CPU架构和.NET框架中,volatile多多少少是冗余的。Joe Duffy在这里讨论。http://www.bluebytesoftware.com/blog/2010/12/04/SayonaraVolatile.aspx - stevethethread
遗憾的是,即使通常使用volatile一切都正常,但volatile write并不能保证写操作在宇宙热寂之前完成 :-) - xanatos
@xanatos:实际上,在这里,您需要使用“volatile”进行读取,因为即使没有关键字,在.NET中写入也是易失性的。 - Tudor
使用Volatile关键字仅是为了防止内存读/写的重新排序,大多数现代CPU架构已经强制执行此操作,.net框架也是如此。标记变量为volatile的关键之处在于对该变量的所有读取和写入都是volatile的,这可能过于严格,因此性能不佳。如果只在一个地方相关,则简单的锁定就足够了。 - stevethethread
@stevethethread:这更多是关于防止缓存而不是重新排序。锁比易失性变量更加沉重,因为您需要支付锁定-解锁开销和潜在的上下文切换。 - Tudor
显示剩余5条评论

0
你最好直接在变量上加锁,例如:
private static readonly object _lock = new Object();

....

lock(_lock){
//access to boolean variable etc.
}

另一种可能性是将bool包装在Lazy中,访问内部值是线程安全的。
如果您想使用无锁机制来读取和更新值,则可以考虑使用Interlocked类中的方法。
信息在此处:

System.Threading.Interlocked


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