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

时间:2009-10-07 13:31:18

标签: c# multithreading

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

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

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

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

中的总滴答
class x
{
  long _lastHit;

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

4 个答案:

答案 0 :(得分:9)

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

long _lastHit;

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

以原子方式阅读:

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

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

简单阅读并不好 - 至少因为变量未标记为易失性,因此后续读取可能只是重用缓存值。结合挥发性和互锁将可能在这里工作(我不完全确定,但我认为即使另一个核心进行非互锁读取,也不能在不一致的状态下看到互锁写入)。但是如果你这样做,你会得到警告和代码味道,结合两种不同的技术。

选项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

答案 1 :(得分:2)

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

答案 2 :(得分:0)

编辑:基于以下评论来自@romkyns [Thanks]

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

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

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

答案 3 :(得分:0)

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

Word Wrap

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

class x
{
  string _lastHit;

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

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