易变的日期时间
Posted
技术标签:
【中文标题】易变的日期时间【英文标题】:Volatile DateTime 【发布时间】:2011-06-17 08:35:03 【问题描述】:DateTime
不能声明为 volatile,对吗?:
private DateTime _time;
public DateTime Time
get
Thread.MemoryBarrier();
return _time;
set
_time = value;
Thread.MemoryBarrier();
可以从不同的线程访问该属性,因此我想确保它们始终获得最新版本,而不使用争用(锁定)。
编辑:
我有一组难以创建的项目,每个项目都有一个名为 CreationTime 的 DateTime 属性,指示该项目的创建时间。它被初始化为 DateTime.UtcNow。 每次访问项目时,该属性都会更新为 DateTime.UtcNow。 有一个线程在线程计时器中及时执行,检查是否 (DateTime.UtcNow + 1 小时) > item.CreationTime,如果为真则删除该项目。我想确保当“删除线程”进入集合时,所有项目都有其最新的“最后访问”日期时间,这样我就可以避免仅仅因为缓存保存了 a 的值而再次创建项目几毫秒:D
提前致谢。
【问题讨论】:
鉴于您正在实现一个缓存,该缓存会删除超过特定时间段的未使用对象,我认为InterlockedExchange
解决方案是这里的方法。
您知道 DateTime.Now 仅精确到 1/64 秒,对吧?如果您担心由于缓存延迟毫秒而导致的不准确性,那么您已经迷路了;您试图保持准确的数据的精度始终远低于 1/1000 秒。
@Eric,问题:DateTime.Now 没有列出 1/64 秒。记录在哪里?
@yodaj007:它也是。它说在操作系统 NT3.5 和更高版本上,精度约为 10 毫秒,或 1/100 秒。 “大约”的意思是“不完全”;允许操作系统决定适当的精度是多少。出于显而易见的原因,它通常接近线程量子。在我的机器上精确到 1/64 秒;你的机器可能更好或更糟。
@Eric:明白了。我计算的是 1/64 = 0.015 秒,即 15.6 毫秒。但是我没有看到“近似”这个词。谢谢。
【参考方案1】:
没错。
但是,您还有另一个选择。将时间存储为 Int64 滴答计数,并使用InterlockedExchange
进行设置。然后线程可以使用Int64 构造函数构造它们自己的DateTime
,这样就不会发生争用和锁定。
编辑:
鉴于您提供了更多信息,现在提供示例会更容易。
public class Cache
class CacheEntry
private Int64 m_Touched;
public CacheEntry()
Touch();
public void Touch()
System.Threading.Interlocked.Exchange(ref m_Touched, DateTime.Now.Ticks);
public DateTime Touched
get
return new DateTime(Interlocked.Read(ref m_Touched));
// eo class CacheEntry
// eo class Cache
【讨论】:
最好使用ToBinary
/FromBinary
而不是仅仅存储原始报价,这样DateTimeKind
也会被保留。 (虽然我认为 OP 应该只使用 lock
和普通的 DateTime
而不是试图过于聪明。)
虽然可能,但这两个建议听起来可能涉及很多冗余转换。当然,这取决于 DateTime 值有多少消费者。
@Sensai76,也许,但我怀疑转换将是一个麻烦的开销(尽管很难说不知道其他线程如何使用它),也就是说,它会更少比某种锁的开销更大。
您正在以原子方式设置值,但不能保证Touched
属性会以原子方式读取它。
我认为 DateTime(Interlocked.Read(ref m_Touched)); 前面缺少了一个新关键字。【参考方案2】:
您的代码不是线程安全的,因为DateTime
的分配不能保证是原子的。一般来说,最多 32 位的整数赋值是原子的,但 64 位不一定是原子的。
您可能可以将 Interlocked.Exchange
与 DateTime
的刻度一起使用,因为它可以原子地存储 Int64。
但是如果你切换到刻度,你需要知道只有 62 位用于刻度,2 位用于种类。所以你不会失去那种。
即使你让 getter 和 setter 原子成为线程安全的,我也不确定这是否足够。因为时间可以在你的吸气剂返回的时间和你实际使用你得到的时间的时间之间变化。所以你的时间总是过时的。
【讨论】:
这是一个用于 x64 机器的 x64 应用程序。有关系吗?好点子。 @vtortola 根据这个,64 位赋值可以是原子的:CLI 保证读取和写入值类型的变量是处理器的自然指针大小(或更小)的大小是原子的;如果您在 64 位版本的 CLR 中在 64 位操作系统上运行 C# 代码,那么 64 位双精度和长整数的读写也保证是原子的。 C# 语言不保证这一点,但运行时规范可以。 ***.com/a/11745471/67824【参考方案3】:这是不可能的 - 您需要使用 lock
或 Monitor
类来同步对字段的访问。
这是因为DateTime
是一种值类型——一种结构。
来自 MSDN - volatile (C# Reference):
volatile 关键字可以应用于以下类型的字段:
引用类型。 指针类型(在不安全的上下文中)。请注意,尽管指针本身可以是易失的,但它指向的对象不能。换句话说,您不能声明“指向 volatile 的指针”。 sbyte、byte、short、ushort、int、uint、char、float 和 bool 等类型。 具有以下基本类型之一的枚举类型:byte、sbyte、short、ushort、int 或 uint。 已知为引用类型的泛型类型参数。 IntPtr 和 UIntPtr。
正如其他人所提到的,您可以使用Ticks
来跟踪时间。
【讨论】:
以上是关于易变的日期时间的主要内容,如果未能解决你的问题,请参考以下文章