2017-02-15 10 views
52

В параметре C# значение переменной является атомарным, если его размер не превышает native int (т. Е. 4 байта в 32-битной среде выполнения и 8 байтов на 64-разрядной версии). В 64-битной среде, которая включает в себя все типы ссылок и большинство встроенных типов значений (byte, short, int, long и т. Д.).Может ли DateTime разрываться в 64-битной среде?

Установка большего значения не является атомарной и может вызвать разрывы, когда обновляется только часть памяти.

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(); 
} 
+0

, вероятно, самый окончательный ответ, который вы можете получить: «Я так не думаю». –

+3

@MikeNakis Я также «не думаю так», но реализация ConcurrentDictionary' не обрабатывает «DateTime» как атомную, что заставляет меня задаться вопросом: [ConcurrentDictionary.IsValueWriteAtomic] (https://github.com/dotnet/ corefx/blob/master/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs # L87) – i3arnon

+4

@ i3arnon: в коде перечислены кучи интегральных типов с комментарием, указывающим на ссылку CLI. Ссылка на CLI касается только размеров, а не типов, и я серьезно сомневаюсь, что DateTime когда-либо будет использовать явно несогласованное поле, поэтому я думаю, что можно с уверенностью заключить, что 'DateTime' является атомарным. –

ответ

32

В разделе ECMA specification «I.12.6.6 Atomic читает и пишет»

Соответствующий CLI гарантирует, что чтение и запись доступ к выравнивать ячейки памяти не больше, чем родной размер слова (размер типа native int) является атомарным (см. п. 1.1.6.2), когда все обращения к записи в одном месте имеют одинаковый размер. Атомные записи не должны изменять никаких битов, кроме написанных. Если для изменения поведения по умолчанию не используется явное управление компоновкой (см. Раздел II («Управление экземпляром макета»), элементы данных, размер которых не превышает размер естественного слова (размер native int), должны быть правильно выровнены. Ссылки на объекты должны обрабатываться так, как если бы они были сохранены в размере родного слова.

A native int является в C#.

Пока sizeof(IntPtr) >= sizeof(DateTime) верно для среды выполнения (он же: работает как 64 бит), и они не изменяют внутреннюю структуру, чтобы быть явным макетом с неровными байтами вместо [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 
} 
+0

Но вопрос стоит: Is 'sizeof (IntPtr) == sizeof (DateTime)'? 'DateTime' содержит только' ulong', но означает ли это 'sizeof (DateTime) == sizeof (ulong) == sizeof (IntPtr) == 8'? – i3arnon

+0

Да, все 3 выхода 8 в 64-битной среде, therfor являются атомарными в соответствии со спецификацией. В 32-битной среде 'DateTime' и' ulong' возвращают 8, а 'IntPtr' возвращает 4, что нарушает правило' sizeof (IntPtr)> = sizeOf (DateTime), так что там нет гарантированного атома (это то, что мы ожидали чтобы произойти) –

+0

Подождите, что вы имеете в виду все 3 выхода 8? Как вы запускаете 'sizeof (DateTime)'? Я не могу скомпилировать это и получить «' DateTime' не имеет предопределенного размера ». Вы можете использовать 'Marshal.SizeOf', но проверяете размер при сортировке. – i3arnon

-1

Из спецификации # языка Си.

5,5 Атомарность переменных ссылок Чтение и запись следующих типов данных являются атомарными: BOOL, полукокса, байт, SByte, короткие, UShort, UINT, INT, поплавок, и ссылочные типы. Кроме того, чтение и запись перечислений типов с базовым типом в предыдущем списке также являются атомарными. Считывание и запись других типов, в том числе long, ulong, double и десятичные, а также определяемые пользователем типы, не гарантируются atomic. Помимо библиотечных функций, предназначенных для этой цели, нет гарантии атомарного чтения-модификации-записи, например, в случае приращения или уменьшения.

+4

Правда в принципе, но мы часто создаем типы, которые обеспечивают более строгие гарантии, поэтому это не отвечает на вопрос. –

8

Выполнение некоторых тестов и на основе above answer вполне можно сказать, что это атомный сегодня.

Я написал тест, чтобы проверить, сколько слез может быть найдено во время X итераций по N потокам для Int64, DateTime и 3 пользовательских структур размером 128, 192 и 256 - ни один из них не перепутался со StructLayout.

Тест состоит из:

  1. Добавление набора значений в массиве таким образом, они известны.
  2. Настройка одного потока для каждой позиции массива, этот поток назначит значение из массива общей переменной.
  3. Настройка того же количества потоков (array.length) для чтения из этой общей переменной в локальную.
  4. Проверьте, находится ли это локальное содержимое в исходном массиве.

Результаты являются в моей машине (ядро i7-4500U, Windows 10 x64, .NET 4.6, релиз без отладки, целевая платформа: 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