Can DateTime tear in a 64 bit environment?
在C中,只要变量的大小最多为native int(即32位运行时环境中的4个字节,64位环境中的8个字节),那么为变量设置值就是原子的。在包含所有引用类型和大多数内置值类型(byte、short、int、long等)的64位环境中。
设置一个更大的值不是原子的,并且在只有部分内存被更新的地方会导致撕裂。
DateTime是一个结构,它只包含一个包含所有数据(Ticks和DateTimeKind的单个ulong字段,ulong本身在64位环境中是原子的。
这是否意味着DateTime也是原子的?或者下面的代码会在某个时刻导致撕裂吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static DateTime _value ;
static void Main ()
{
for (int i = 0; i < 10; i ++)
{
new Thread (_ =>
{
var random = new Random ();
while (true)
{
_value = new DateTime ((long)random .Next() << 30 | (long)random .Next());
}
}).Start();
}
Console .ReadLine();
} |
- 也许你能得到最明确的答案:"我不这么认为。"
- @Mikenakis我也"不这么认为",但ConcurrentDictionary的实现并没有将DateTime视为原子,这让我怀疑:ConcurrentDictionary.IsValueWriteAtomic
- @i3arnon:代码列出了一系列整型,并带有指向cli引用的注释。cli引用只处理大小,而不是类型,我严重怀疑datetime是否会使用显式不对齐的字段,因此我认为可以安全地得出结论:DateTime是原子的。
- 这很奇怪。标准规定,对不大于本机int大小的值的所有修改都应是原子的,并且DateTime适合64位系统上的这一类别。然而,ConcurrentDictionary似乎比标准保守得多:它不会将不大于native int struct的数据视为原子数据,它只将内置的原始数据类型视为原子数据。传统的观点认为这可能是ConcurrentDictionary的一个缺陷,但请注意我是如何写评论而不是回答的!C=
- 对我来说,更大的问题是,"你为什么在乎?"为什么要花这么多时间去弄清楚它,而不是仅仅假设它不安全,然后在这个假设下编写代码?我不确定我会指望微软遵守规范,即使它说它是安全的。;)所以我觉得这是一个xy问题。
- @JPMC26只是设置一个值而不是使用一个锁,在高度并发的场景中是非常有益的。我之所以关心,是因为关心能让我突破瓶颈,提高产品性能。
- @为什么这是个问题?设置自动布局是什么原因?
- @13警告-可能是这样有趣的事情。我不知道其他的恶作剧。就我个人而言,我更不喜欢DateTime等人。以及API的形状(不幸的是,对于简单的情况来说,它的效果刚好足够好)。有一些随机的问题使得某些用例无法本地解决。
- @i3如果有疑问,我会将勾号存储在long字段中,并将其包装在DateTime属性中。零风险。
- 我写了一个IT Gist.github.com/flash3001/ec0da534167bd3cd85bcfc12eff0838的可测试版本
- 不,日期时间不是原子的吗?我希望,因为datetime是一个结构规则适用的结构。它的基础值恰好使用本机单词大小这一事实并不相关。除了当一个stuct被分配/被"构造"的时候,我不应该假设它受到跨领域并发问题的影响吗?旁注:我发现内置的.NET互锁类(system.threading.interlocked)在现实世界中非常有用,我知道我在32位内核上的数千个32位运行时上使用int64。
From the ECMA specification section"I.12.6.6 Atomic reads and writes"
A conforming CLI shall guarantee that read and write access to properly aligned memory locations no larger than the native word size (the size of type native int) is atomic (see §I.12.6.2) when all the write accesses to a location are the same size. Atomic writes shall alter no bits other than those written. Unless explicit layout control (see Partition II (Controlling Instance Layout)) is used to alter the default behavior, data elements no larger than the natural word size (the size of a native int) shall be properly aligned. Object references shall be treated as though they are stored in the native word size.
这是一个EDOCX1。
So long as sizeof(IntPtr) >= sizeof(DateTime)is true for the runtime environment(AKA:running as 64 bit),and they don't alter the internal structure to be explicited layout with mislained bytes instead of the [StructLayout(LayoutKind.Auto)]it currently has,then reads and writes of a DateTimeECMA规范
在64位环境中运行以下代码是可以核查的:
1 2 3 4 5 6
| public unsafe static void Main ()
{
Console .WriteLine(sizeof(DateTime )); // Outputs 8
Console .WriteLine(sizeof(IntPtr )); // Outputs 8
Console .WriteLine(sizeof(ulong)); // Outputs 8
} |
- 但问题是:sizeof(IntPtr) == sizeof(DateTime)是不是?DateTime只包含ulong,但这是否意味着sizeof(DateTime) == sizeof(ulong) == sizeof(IntPtr) == 8?
- 是的,所有3个输出8在64位环境中,根据规范,它们都是原子的。在32位环境中,DateTime和ulong返回8,但IntPtr返回4,这违反了sizeof(IntPtr) >= sizeOf(DateTime)规则,因此没有保证原子(这是我们预期的情况)。
- 等等,你说的3输出8是什么意思?你是怎么运行sizeof(DateTime)的?我不能编译它并得到"DateTime没有预先定义的大小"。您可以使用Marshal.SizeOf,但在编组时测试大小。
- IsReadsAndWritesGuaranteedAtomic->AreReadsAndWritesGuaranteedAtomic?或者也许ReadsAndWritesAreGuaranteedAtomic会更好。
- @i3arnon这里是我运行的确切代码,您只需要在项目生成设置中将程序集标记为不安全,但是我在结尾处更新了示例函数,以使用不安全的代码而不是marshal类,因为您确实指出了潜在的大小差异。
- 您还可以使用nuget包System.Runtime.CompilerServices.Unsafe并调用任何类型的Unsafe.SizeOf()。这个库是用IL编写的,它公开了一些在C_中不可用的低级功能。
- @Scottchamberlain你介意我(或你)在答案中加上这个代码吗?我认为能够运行sizeof(DateTime)是我们得到的最明确的答案。
- @我一点也不担心,别紧张。
- 这里的AreReadsAndWritesAreGuaranteedAtomic方法不检查只有一个字段(一个带有两个int字段的struct不需要作为一个64位写入写入写入,而是包含两个可以作为两个写入写入的内存位置,这些写入是单独原子的,而不是成对原子的),也不检查EDOCX1的存在。20℃。
- @jonhana type.IsAutoLayout会检查StructLayoutAttribute,这是一个不需要提取属性的便利属性。不过,我将给出两个int示例,它将在64位机器上返回一个sizeof(example) == 8,并且不会工作。我已经从问题中去掉了这个功能。
- 是的,你在江户记1〔24〕上,我的眼睛不知怎么地跳过了那一点。
- @Jonhanna想得更多,我不知道clr是如何处理两个int的情况的,它可以将结构作为一个8字节的对象而不是两个4字节的对象来处理,这样它仍然是原子的。基于规范还不清楚,我仍然会关闭函数,但它可能值得测试。
运行一些测试,并以用户回答为基础,很安全地说它今天是原子。
我写了一份测试,以确定在X个项目中,在NT64、Datetime和3个128、192和256 sizes-none的定制结构中,能找到多少泪水。
The test consists of:
将一组数值添加到一个阵列,因此它们是众所周知的。
为每个阵列位置设置一个螺纹,该螺纹将指定从阵列到共享变量的值。
Setting up the same number of threads(array.length)to read from this shared variable to a local.
检查这地方是否包含在原始阵列中。
The results are as follows in my machine(Core I7-4500U,Windows 10 X64,Net 4.6,release without debug,Platform target:X64 with code optimization):
ZZU1
测试代码可以在这里找到:https://gist.github.com/flash3001/da5bd3ca800f674082d8030EF70CF4
语言规范。
BLCK1/
- 原则上是正确的,但是我们经常构建提供更严格保证的类型,所以这并不能回答问题。