.NET DateTime线程安全吗?

18

.NET DateTime线程安全吗?我不担心读取操作返回的值是否正确,我唯一关心的是:如果未同步,DateTime对象是否会损坏。


是的,我正在从多个线程分配给DateTime。在这种情况下,它会抛出异常吗?还是唯一的缺点是我们无法预测哪个线程的赋值操作执行得最新? - user632942
3
这不安全。底层的值是 long 类型,在 32 位操作系统上不是原子性的。 - Hans Passant
2
您可以获得两个写入的混合。一个写入的高32位部分和另一个写入的低32位部分。在32位系统上,64位值的操作不是原子性的。 - CodesInChaos
@CodesInChaos:你的意思是说,如果通过线程访问Date/Now/TimeStamp,你可能会得到完全损坏的结果? - xfx
3
那么在64位系统上运行是安全的吗? - Brain2000
显示剩余2条评论
2个回答

23

读取和写入DateTime字段不是原子的(至少在32位系统上不是)。

  • 如果您在同一时间向多个线程分配同一属性,则可能会损坏它。

  • 如果您从一个线程读取,并从另一个线程写入,则读取线程可能会获得损坏的值。

  • 在没有同时有写入线程的情况下从多个线程读取是安全的。

实际上,当多个线程同时使用时,DateTime的两个32位半部分可能包含不同年龄的值。

您可以获得两个写入的混合体。一个写入的高32位部分和另一个写入的低32位部分。

作为替代方案,您可以使用Int64作为该字段,并使用ThreadInterlocked的原子方法来处理它。然后使用new DateTime(ticks)dateTime.Ticks进行DateTime的转换。

MSDN说:

此类型的所有成员都是线程安全的。看起来修改实例状态的成员实际上返回用新值初始化的新实例。与任何其他类型一样,读取和写入包含此类型实例的共享变量必须受锁的保护,以保证线程安全

在所有硬件平台上,分配此类型的实例并非线程安全,因为该实例的二进制表示形式可能太大而无法在单个原子操作中分配。


但是DateTime是不可变的,因此它的字段永远不会被更改。或者我有什么遗漏吗? - Robert
3
在一个不可变的结构体中,无法单独修改字段。但是可以替换变量的内容。这个替换操作本质上复制了所有字段。这个替换不一定是原子性的。这与不可变值类型不同,因为重新分配变量只会复制引用而不是内容。 - CodesInChaos
3
不要使用Ticks,您可以使用DateTime.FromBinary(longValue)和dateTime.ToBinary()。这个值仍然适合于Int64,并且它将保留Kind属性。 - Neil
@CodesInChaos先前的评论说这与不可变值类型不同,但它应该是这与不可变引用类型不同, - mjwills
如果您的值是一个结构体,比如DateTime,在ConcurrentDictionary中,并且您更新了一个键的值,会发生什么?如果它在普通字典中呢?ConcurrentDictionary是否会在整个读/写KeyValuePair的持续时间内锁定该值,确保它被原子地读取? - Triynko

13

DateTime是一个不可变的值类型(结构体)。一旦创建,就无法更改。

它不会被损坏且可以在多线程中安全使用。

如果您从多个线程(无论读取/写入还是仅写入)更改DateTime变量,则需要进行同步 - 因为此操作不是线程安全的。


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