使用Interlocked进行线程安全的DateTime更新。*

我可以使用Interlocked。*同步方法来更新DateTime变量吗?

我希望在内存中保留最后触摸时间戳。 多个http线程将更新最后一次触摸DateTime变量。

我很欣赏DateTime变量是被替换而不是更新的值类型。

我能想到的最好的方法是将时间戳保持为长整数

class x { long _lastHit; void Touch() { Interlocked.Exchange( ref _lastHit, DateTime.Now.Ticks ); } } 

是的,你可以这样做。 你最大的问题可能是DateTime.Ticks只有大约20毫秒的分辨率。 因此,如果您保留DateTime lastlong ticks变量并不重要。 但由于DateTime没有Exchange重载,因此需要使用long

选项1 :使用带有InterlockedDateTime.ToBinary()long 。 这不需要volatile (事实上​​,如果你有它,你会收到警告)因为Interlocked已经确保了primefaces更新。 您可以通过这种方式获得DateTime的确切值。

 long _lastHit; void Touch() { Interlocked.Exchange(ref _lastHit, DateTime.Now.ToBinary()); } 

以primefaces方式阅读:

 DateTime GetLastHit() { long lastHit = Interlocked.CompareExchange(ref _lastHit, 0, 0); return DateTime.FromBinary(lastHit); } 

这将返回_lastHit的值,如果为0,则将其与0交换(即除了primefaces读取值之外什么都不做)。

简单地读取是不好的 – 至少因为变量没有标记为volatile,所以后续读取可能只是重用缓存的值。 将volatile和Interlocked组合在一起可能会起作用(我不完全确定,但我认为即使另一个内核执行非互锁读取,也不能在不一致的状态下看到互锁写入)。 但是,如果你这样做,你会得到一个警告和代码气味,结合两种不同的技术。

选项2 :使用锁。 在这种情况下不太理想,因为在这种情况下,互锁方法更具性能。 但是你可以存储正确的类型,而且它更清晰:

 DateTime _lastHit; object _lock = new object(); void Touch() { lock (_lock) _lastHit = DateTime.Now; } 

必须使用锁来读取这个值! 顺便提一下,除了互斥之外,锁还确保不能看到缓存的值并且不能重新排序读/写。

非选项 :不执行任何操作(只需写入值),无论是否将其标记为volatile 。 这是错误的 – 即使您从未读过该值,您在32位计算机上的写入可能会以如此不幸的方式交错,从而导致损坏的值:

 Thread1: writes dword 1 of value 1 Thread2: writes dword 1 of value 2 Thread2: writes dword 2 of value 2 Thread1: writes dword 2 of value 1 Result: dword 1 is for value 2, while dword 2 is for value 1 

编辑:根据@romkyns下面的评论[谢谢]

如果您的代码在32位计算机上运行。 然后在两个primefaces操作中将64位长写入内存,这可以被上下文切换中断。 所以一般来说你需要处理这个问题。

但要明确的是,对于这个特定的场景,(写一个代表时间刻度的长值),可以说这个问题是如此非常无情,以至于不值得处理…因为(除了每一秒一瞬间)无论如何,高位字(32位)中的值对于任何两个并发写入都是相同的…甚至在极不可能的事件中,有两个并发写入跨越该边界,这同时中断彼此,你从一个得到hi字,从另一个得到低字,除非你每隔millesecond也读这个值,下一次写将无论如何都会解决问题,并且不会造成任何伤害。 然而,采用这种方法,无论坏的情况多么不可能,仍然允许极其微小但可能的情况下,每隔40亿个滴答中就有一个错误的值……(并且好运试图重现那个错误…)

如果你在64位机器上运行,otoh(更可能在这个时间点但不能保证)那么64位内存插槽中的值是primefaces地写的,你不需要担心这里的并发性。 竞争条件(这是您要阻止的)只有在某些程序不变的情况下才会发生,该程序不变状态在某些处理块中可能被另一个线程中断。 如果你所做的只是写入lastTouch DateTime变量(内存位置),那么就没有这样的invlaid不变量,因此你不必担心并发访问。

我的方法不是最好的方法之一,但您可以使用字符串var来存储格式化日期,然后将其解析回datetime:

 class x { string _lastHit; void Touch() { Interlocked.Exchange( ref _lastHit, DateTime.Now.ToString("format your date") ); } } 

当您需要使用此值时,只需解析为DateTime:

 DateTime.Parse(_lastHit) 

解析始终有效,因为字符串是使用DateTime类格式化的,但您可以使用TryParse来处理可能的解析错误