我正在C#中构建一个多线程缓存,它将包含一个Car对象列表:
public static IList<Car> Cars {get; private set;}
我想知道在没有锁定的情况下更改线程中的引用是否安全?
e.g。
private static void Loop()
{
while (true)
{
Cars = GetFreshListFromServer();
Thread.Sleep(SomeInterval);
}
}
基本上,它归结为是否为汽车分配新的参考资料是否是原子的我猜。
如果不是,我显然必须为我的汽车使用私人领域,并锁定获取和设置。
答案 0 :(得分:59)
是的,参考更新在语言规范中保证是原子的。
5.5变量引用的原子性
以下数据类型的读写是原子的:bool,char,byte,sbyte,short,ushort,uint,int,float和reference类型。此外,在先前列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的。
然而,在紧密循环中,可能被寄存器缓存咬住。在这种情况下不太可能,除非您的方法调用内联(可能会发生)。我个人会添加lock
来简化和预测,但volatile
也可以在这里提供帮助。请注意,完整的线程安全不仅仅是原子性。
对于缓存,我会亲自查看Interlocked.CompareExchange
- 即尝试进行更新,但如果失败则重做从头开始> strong>(从新值开始)再试一次。
答案 1 :(得分:0)
在@Marc Gravell的回答中,如C#语言规范5.5所述,重要的是要知道术语“用户定义类型”的含义。我没有找到明确的定义w.r.t.这种用法在C#语言规范中。在UML和通常的说法中,Class是Type的实例。但是在C#语言规范的背景下,它的含义还不清楚。
Visual Basic语言参考部分“用户定义的类型”(在https://msdn.microsoft.com/en-us/library/cec05s9z.aspx)说
“以前版本的Visual Basic支持用户定义类型(UDT)。当前版本将UDT扩展为结构。”
所以用户定义的Type似乎是一个结构,而不是一个Class。
但是......
根据“C#编程指南”部分“类型”(https://msdn.microsoft.com/en-us/library/ms173104.aspx):
“典型的C#程序使用类库中的类型以及 用户定义的类型“
表示Class是用户定义的类型。稍后,它给出了“复杂的用户定义类型:”
的示例MyClass myClass;
表示“MyClass”是用户定义的类型。后来它说:
“CTS中的每种类型都被定义为值类型或引用 类型。这包括.NET Framework类中的所有自定义类型 库和也是您自己的用户定义类型。 “
...这意味着开发人员创建的所有类都是“用户定义的类型”。
最后,有一个Stackoverflow项目,其中对该术语的含义进行了无可争议的辩论:How do I determine if a property is a user-defined type in C#?
因此,为了安全起见,我被迫考虑所有类,无论是我创建的类还是.Net Framework中的类,都是用户定义的类型,因此不是线程安全的分配,因为它说在C#语言规范第5.5节中:
读取和写入以及用户定义的类型并不保证是原子的。
不幸的是,在C#语言规范等精确规范中使用了口语术语。由于这种模糊性,为了保证线程安全,我可能编写的代码不是最好的,如果事实证明“用户定义的类型”不包括CLR类。
因此,我要求进一步澄清这个stackoverflow答案,因为它的答案的当前基础留下了这种显着的模糊性。就目前而言,问题“参考作业线程安全吗?”的答案似乎是“ NO ”。