Может ли DateTime разрываться в 64-битной среде?
В параметре С# значение переменной равно атомному, если его размер не превышает native int
(т.е. 4 байта в 32-битной среде выполнения и 8 байтов на 64-битной).
В 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();
}
Ответы
Ответ 1
Из спецификация ECMA раздел "I.12.6.6 Атомные чтения и записи"
Соответствующий CLI должен гарантировать, что доступ для чтения и записи к правильно выровненным ячейкам памяти не превышает размер родного слова (размер типа native int
) является атомарным (см. п. 1.1.6.2), когда все обращения к записи к местоположению одинакового размера. Атомные записи не должны изменять никаких битов, кроме написанных. Если для изменения поведения по умолчанию не используется явное управление компоновкой (см. Раздел II ( "Управление макетом экземпляра" ), элементы данных, не превышающие размер естественного слова (размер native int
), должны быть правильно выровнены. Ссылки на объекты должны обрабатываться так, как если бы они были сохранены в размере родного слова.
A native int
является IntPtr
в С#.
Пока sizeof(IntPtr) >= sizeof(DateTime)
истинно для среды выполнения (aka: running as 64 bit), и они не изменяют внутреннюю структуру, чтобы быть явным макетом с несогласованными байтами вместо [StructLayout(LayoutKind.Auto)]
, который он имеет в настоящее время, то чтение и запись структуры DateTime
(или любой другой структуры, которая следует этим правилам) гарантированно будут атомарными по спецификации ECMA.
Вы можете убедиться, что, выполнив следующий код в 64-разрядной среде:
public unsafe static void Main()
{
Console.WriteLine(sizeof(DateTime)); // Outputs 8
Console.WriteLine(sizeof(IntPtr)); // Outputs 8
Console.WriteLine(sizeof(ulong)); // Outputs 8
}
Ответ 2
Выполняя некоторые тесты и основываясь на ответе выше, вполне можно сказать, что сегодня он атомный.
Я написал тест, чтобы проверить, сколько слез может быть найдено во время X-итераций по N потокам для Int64, DateTime и 3 пользовательских структур из 128, 192 и 256 размеров - ни один из них не испорчен в StructLayout.
Тест состоит из:
- Добавление набора значений в массив, чтобы они были известны.
- Настройка одного потока для каждой позиции массива, этот поток присваивает значение из массива общей переменной.
- Настройка того же количества потоков (array.length) для чтения из этой общей переменной в локальную.
- Проверьте, содержится ли этот локал в исходном массиве.
Результаты на моем компьютере следующие: Core i7-4500U, Windows 10 x64,.NET 4.6, Release без отладки, Платформа: x64 с оптимизацией кода):
-------------- Trying to Tear --------------
Running: 64bits
Max Threads: 30
Max Reruns: 10
Iterations per Thread: 20000
--------------------------------------------
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
23 Struct128 (128bits)
87 Struct192 (192bits)
43 Struct256 (256bits)
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
44 Struct128 (128bits)
59 Struct192 (192bits)
52 Struct256 (256bits)
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
26 Struct128 (128bits)
53 Struct192 (192bits)
45 Struct256 (256bits)
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
46 Struct128 (128bits)
57 Struct192 (192bits)
56 Struct256 (256bits)
------------------- End --------------------
Код для теста можно найти здесь: https://gist.github.com/Flash3001/da5bd3ca800f674082dd8030ef70cf4e
Ответ 3
Из спецификации языка С#.
5.5 Атоматичность переменных ссылок Считывание и запись следующих типов данных являются атомарными: bool, char, byte, sbyte, short, ushort, uint, int, float и reference. Кроме того, чтение и запись перечисления типы с базовым типом в предыдущем списке также являются атомарными. Читает и пишет другие типы, включая long, ulong, double и десятичные, а также пользовательские типы не гарантируются. атомное. Помимо библиотечных функций, предназначенных для этой цели, нет гарантии атомного чтения-модификации-записи, например, в случае прироста или уменьшения.