我正在用C#构建一个多线程缓存,它将保存一个汽车对象列表:
public static IList<Car> Cars {get; private set;}我想知道在不加锁的情况下更改线程中的引用是否安全?
例如:
private static void Loop()
{
while (true)
{
Cars = GetFreshListFromServer();
Thread.Sleep(SomeInterval);
}
}基本上,这归结于给汽车分配一个新的引用是否是原子的,我猜。
如果不是,我显然将不得不为我的汽车使用私人领域,并锁定获取和设置。
发布于 2011-03-06 17:08:06
是的,在语言规范中保证引用更新是原子的。
5.5变量引用的原子性
以下数据类型的读取和写入是原子的: bool、char、byte、sbyte、short、ushort、uint、int、float和reference类型。此外,对前面列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型(包括long、ulong、double和decimal )以及用户定义类型的读取和写入不能保证是原子的。
然而,在紧凑的循环中,您可能会被寄存器缓存所吞噬。在这种情况下不太可能,除非你的方法调用是内联的(这是可能发生的)。就我个人而言,我会添加lock来使它变得简单和可预测,但volatile在这方面也可以提供帮助。请注意,完全的线程安全不仅仅是原子性。
在缓存的情况下,我会亲自查看Interlocked.CompareExchange -即尝试更新,但如果失败,请从头开始重新执行(从新值开始),然后重试。
发布于 2015-04-25 03:56:46
在@Marc Gravell的回答中,正如C#语言规范5.5中所引用的那样,了解术语“用户定义类型”的含义是很重要的。我还没有找到一个明确的w.r.t定义。这在C#语言规范中的用法。在UML和通俗的说法中,类是类型的实例。但在C#语言规范的上下文中,它的含义并不明确。
Visual Basic语言参考部分“用户定义的类型”(在https://msdn.microsoft.com/en-us/library/cec05s9z.aspx)中说
“早期版本的Visual Basic支持用户定义类型( UDT )。当前版本将UDT扩展为结构。”
因此,用户定义的类型似乎是一个结构,而不是一个类。
但是...
根据"C#编程指南“部分”类型“(在https://msdn.microsoft.com/en-us/library/ms173104.aspx上):
“典型的C#程序使用类库中的类型以及用户定义的类型”
这意味着Class是用户定义的类型。稍后,它给出了一个“复杂的用户定义类型:”
MyClass myClass;
这意味着"MyClass“是用户定义的类型。后来它又说:
“CTS中的每个类型都被定义为值类型或引用类型。这包括.NET框架类库中的所有自定义类型以及您自己的用户定义类型。”
..。这意味着由开发人员创建的所有类都是“用户定义类型”。
最后,还有一个Stackoverflow项目,在这个项目中,这个术语的含义没有定论:How do I determine if a property is a user-defined type in C#?
因此,为了安全起见,我不得不考虑所有类,无论是我创建的类还是在.Net框架中找到的类,都是用户定义的类型,因此对于分配来说不是线程安全的,因为它在C#语言规范第5.5节中写道:
读取和写入...和用户定义的类型一样,都不能保证是原子类型。
不幸的是,在像C#语言规范这样的精确规范中使用了口语化的术语。由于这种模糊性,为了保证线程安全,我编写的代码可能比"User-defined Type“不包含CLR类时可能编写的代码要差。
因此,我要求进一步澄清这个堆栈溢出的答案,因为它的当前基础的答案留下了这个显着的歧义。现在看来,“是引用赋值线程安全?”这个问题的答案似乎是"NO“。
https://stackoverflow.com/questions/5209623
复制相似问题