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

cam*_*ase 12 c# multithreading

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

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

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

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

class x
{
  long _lastHit;

  void Touch()
  {
    Interlocked.Exchange( ref _lastHit, DateTime.Now.Ticks );   
  }
}
Run Code Online (Sandbox Code Playgroud)

Rom*_*kov 9

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

long _lastHit;

void Touch()
{
    Interlocked.Exchange(ref _lastHit, DateTime.Now.ToBinary());
}
Run Code Online (Sandbox Code Playgroud)

要原子地阅读:

DateTime GetLastHit()
{
    long lastHit = Interlocked.CompareExchange(ref _lastHit, 0, 0);
    return DateTime.FromBinary(lastHit);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

DateTime _lastHit;
object _lock = new object();

void Touch()
{
    lock (_lock)
        _lastHit = DateTime.Now;
}
Run Code Online (Sandbox Code Playgroud)

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

非选项:无论是否标记,都不执行任何操作(只需写入值)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
Run Code Online (Sandbox Code Playgroud)

  • 我做了数学考虑Interlocked或不对DateTime的影响,并提出了一些有用的数字.1秒是1000万个刻度,32位边界为429.5秒或7.166分钟.这意味着非互锁写入可能导致时间戳为7分钟太低或太高,每7分钟一次.具有非互锁读取的互锁写入只会导致时间戳为7分钟太高,因为前半部分首先写入 - 大致:值为00019999,thread1变为写入00020000,thread2变为0002999,thread1以00020000结束. (3认同)
  • 现在已经很老了,但是值得向任何阅读@NormanH评论的人指出,私有对象确实具有很强的身份。“弱身份”意味着其他地方的另一段代码可以访问同一实例,尤其是如果另一个应用程序域中的代码可以访问同一实例(例如,在插入字符串的情况下可能会发生)。 (2认同)

Hen*_*man 4

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