【问题标题】:Using the C# Volatile keyword in Threaded Application在线程应用程序中使用 C# Volatile 关键字
【发布时间】:2011-03-22 00:35:25
【问题描述】:

我有一个包含几个数组列表的类。

我的主类创建了这个类的一个新实例。我的主类至少有 2 个线程在我的类中添加和删除,其中包含数组列表。目前一切都运行良好,但我只是想知道是否将我的类与其中的数组列表声明为 volatile eg/

private volatile myclass;
myclass = new myclass();
......
myclass.Add(...)
myclass.Clear(..)

【问题讨论】:

  • volatile 仅适用于字段
  • 你为什么要让它变得不稳定?它会为您的应用程序增加任何价值吗?
  • 现在回答你自己的问题并接受它。您回答了几乎 90% 的问题。

标签: c# .net multithreading .net-3.5 volatile


【解决方案1】:

在本示例中,使用 volatile 关键字不会使您的代码成为线程安全的。 volatile 关键字通常用于确保在读取或写入变量(即类字段)的值时,该变量的最新值要么从主存读取,要么直接写入主存,而不是从缓存中读取(例如CPU 寄存器)例如。 volatile 关键字是一种表示“不要对这个共享字段使用缓存优化”的方式,它消除了线程可能使用字段的本地副本而看不到彼此更新的问题。

在你的情况下,myclass 的值实际上并没有被更新(即你没有重新分配 myclass)所以 volatile 对你没有用,它不是你真正想要创建线程的 myclass 变量的更新 -无论如何,在这种情况下是安全的。

如果您希望更新实际类是线程安全的,那么在“添加”和“清除”周围使用“锁定”是一种直接的选择。这将确保一次只有一个线程可以执行这些操作(更新 myclass 的内部状态),因此不应并行执行。

锁可以如下使用:

private readonly object syncObj = new object(); 
private readonly myclass = new myclass();
......

lock (syncObj)
{
    myclass.Add(...)
}

lock (syncObj)
{
    myclass.Clear(..)
}

您还需要在读取由“添加”更新的状态的任何代码周围添加锁定,如果是这种情况,尽管它没有出现在您的示例代码中。

第一次编写多线程代码时,为什么在添加到集合时需要锁可能并不明显。如果我们以 List 或 ArrayList 为例,那么问题就出现了,因为这些集合在内部使用 Array 作为后备存储,并且会动态地“增长”这个 Array(即通过创建一个新的更大的 Array 并复制旧的内容)调用 Add 时满足容量。这一切都发生在内部,需要维护此数组和变量,例如集合的当前大小(而不是可能更大的实际数组的长度)。因此,如果内部 Array 需要增长,添加到集合可能涉及多个步骤。当以不安全的方式使用多个线程时,多个线程可能会间接导致在添加时发生增长,从而践踏彼此的更新。除了多个线程同时添加的问题外,还存在另一个线程可能正在尝试读取集合的问题内部状态正在改变。使用锁可确保此类操作在不受其他线程干扰的情况下完成。

【讨论】:

  • 像你说的那样在对象周围加锁还是加锁(this)更好。当一个锁被使用并且另一个线程试图访问它时会发生什么。它只是让他们等待吗?
  • @Jon 问题 1:最好锁定私有锁变量而不是“this”,因为“this”对任何引用该类实例,即来自类外部的线程可以锁定对该类的引用,这实际上是同一件事。这是使用“this”的意外后果。
  • @Jon 问题 2:当一个线程试图进入一个繁忙的锁部分(这称为锁争用)时,线程实际上被挂起,然后被唤醒当锁变得空闲时。如果有多个线程在等待,则只有一个线程成功,其余的将再次挂起。当锁没有争用(即忙)时,实际的锁检查和获取非常快。尝试使代码保持高效,但在多线程时,正确性和可读性非常重要。在尝试优化事物之前,先进行正确性和单元测试。
【解决方案2】:

目前代码错误;添加 volatile 关键字不会解决它。在不添加同步的情况下跨线程使用 .NET 类是不安全的。

如果不了解更多关于代码结构的信息,就很难给出直截了当的建议。第一步是在对列表对象的所有访问中开始使用lock 关键字;但是,代码中仍然可能存在不能跨多个线程工作的假设。

可以使用已经对多线程访问安全的集合类,这样可以避免将lock 关键字放在正确的位置,但仍然可能出错。

您可以发布更多代码吗?这样我们就可以提供更具体的建议,使其线程安全。

【讨论】:

  • 这个页面提供了很好的解释:msdn.microsoft.com/en-us/library/573ths2x(VS.90).aspx。但是单独使用线程安全的集合可能无法解决问题。如果您的代码与我的代码相似,它将以不安全的方式处理项目本身。
  • 谢谢。本质上,我的班级有一些数组列表,然后我在班级中创建了添加/删除/清除/查找命令,这些命令转到相关的数组列表并执行其操作。但是,我的班级的这个实例最多可以在 2 个线程上调用 Add/Remove。听起来我需要在我的类的这个实例被使用时锁定它。
最近更新 更多