在此示例中,使用 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 需要增长,添加到集合可能涉及多个步骤。当以不安全的方式使用多线程时,多线程可能会间接导致添加时发生增长,从而践踏彼此的更新。以及多个线程同时添加的问题,还有另一个线程可能试图读取集合的问题而内部状态正在改变。使用锁可确保在不受其他线程干扰的情况下完成此类操作。