在Noda Time v2中,我们正在转向纳秒分辨率.这意味着我们不能再使用8字节整数来表示我们感兴趣的整个时间范围.这促使我调查Noda Time的(很多)结构的内存使用情况,这反过来又导致了我在CLR的对齐决定中发现一点点奇怪.
首先,我意识到这是一个实现决策,并且默认行为可能随时发生变化.我意识到我可以使用[StructLayout]和修改它[FieldOffset],但我想提出一个解决方案,如果可能的话不需要它.
我的核心方案是我有一个struct包含引用类型字段和另外两个值类型字段,其中这些字段是简单的包装器int.我希望,这将被表示为对64位的CLR(8用于参考和4每个其他)16个字节,但由于某些原因它使用24个字节.顺便说一下,我正在使用数组测量空间 - 我知道布局在不同的情况下可能会有所不同,但这感觉就像一个合理的起点.
这是一个展示问题的示例程序:
using System;
using System.Runtime.InteropServices;
#pragma warning disable 0169
struct Int32Wrapper
{
int x;
}
struct TwoInt32s
{
int x, y;
}
struct TwoInt32Wrappers
{
Int32Wrapper x, y;
}
struct RefAndTwoInt32s
{
string text;
int x, y;
}
struct RefAndTwoInt32Wrappers
{
string text;
Int32Wrapper x, y;
}
class Test
{
static void Main()
{
Console.WriteLine("Environment: CLR {0} …Run Code Online (Sandbox Code Playgroud) 在C#设置中,只要变量的大小最大native int(即32位运行时环境中的4个字节和64位上的8个字节),变量的值就是原子级.在一个64位的环境,它包括所有的引用类型和最内置值类型(byte,short,int,long,等等).
设置更大的值不是原子的,并且可能导致仅更新部分内存的撕裂.
DateTime是一个结构,它只包含一个ulong包含其所有数据(Ticks和DateTimeKind)的字段,并且ulong在64位环境中本身是原子的.
这是否意味着它DateTime也是原子的?或者以下代码是否会在某些时候导致撕裂?
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();
}
Run Code Online (Sandbox Code Playgroud) 结构System.DateTime及其表兄System.DateTimeOffset的结构布局类型设置为"自动".这可以看作:
typeof(DateTime).IsAutoLayout /* true */
Run Code Online (Sandbox Code Playgroud)
要么:
typeof(DateTime).StructLayoutAttribute.Value /* Auto */
Run Code Online (Sandbox Code Playgroud)
或者从IL中可以看出它声明:
.class public auto ansi serializable sealed beforefieldinit System.DateTime
¯¯¯¯
Run Code Online (Sandbox Code Playgroud)
通常,使用C#编写的结构(即不是枚举的.NET值类型)将具有"顺序"布局(除非StructLayoutAttribute已应用指定另一个布局).
我通过一些常见的搜索BCL组件,以及DateTime和DateTimeOffset是唯一公开可见的结构,我发现用这个布局.
有谁知道为什么DateTime这个不寻常的结构布局?
运行此代码时:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace StructLayoutTest
{
class Program
{
unsafe static void Main()
{
Console.WriteLine(IntPtr.Size);
Console.WriteLine();
Sequential s = new Sequential();
s.A = 2;
s.B = 3;
s.Bool = true;
s.Long = 6;
s.C.Int32a = 4;
s.C.Int32b = 5;
int* ptr = (int*)&s;
Console.WriteLine(ptr[0]);
Console.WriteLine(ptr[1]);
Console.WriteLine(ptr[2]);
Console.WriteLine(ptr[3]);
Console.WriteLine(ptr[4]);
Console.WriteLine(ptr[5]);
Console.WriteLine(ptr[6]);
Console.WriteLine(ptr[7]); //NB!
Console.WriteLine("Press any key");
Console.ReadKey();
}
[StructLayout(LayoutKind.Explicit)]
struct Explicit
{
[FieldOffset(0)]
public int Int32a;
[FieldOffset(4)]
public int Int32b;
}
[StructLayout(LayoutKind.Sequential, Pack …Run Code Online (Sandbox Code Playgroud) .net ×4
c# ×4
datetime ×2
struct ×2
structlayout ×2
clr ×1
concurrency ×1
tearing ×1
unsafe ×1