c# .net datetime concurrency tearing

c# - ¿Puede DateTime desgarrarse en un entorno de 64 bits?



.net concurrency (3)

Al ejecutar algunas pruebas y según la respuesta anterior , es bastante seguro decir que hoy es atómico.

Escribí una prueba para verificar cuántas lágrimas se podían encontrar durante las iteraciones X sobre N subprocesos para Int64, DateTime y 3 estructuras personalizadas de 128, 192 y 256 tamaños, ninguno con su StructLayout en mal estado.

La prueba consiste en:

  1. Agregar un conjunto de valores a una matriz para que se conozcan.
  2. Al configurar un subproceso para cada posición del conjunto, este subproceso asignará el valor del conjunto a una variable compartida.
  3. Configurar el mismo número de subprocesos (array.length) para leer desde esta variable compartida a un local.
  4. Compruebe si este local está contenido en la matriz original.

Los resultados son los siguientes en mi máquina (Core i7-4500U, Windows 10 x64, .NET 4.6, Release sin depuración, Objetivo de plataforma: x64 con optimización de código):

-------------- 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 --------------------

El código para la prueba se puede encontrar aquí: https://gist.github.com/Flash3001/da5bd3ca800f674082dd8030ef70cf4e

En C #, establecer un valor para una variable es atómico siempre que su tamaño sea como máximo native int (es decir, 4 bytes en un entorno de tiempo de ejecución de 32 bits y 8 bytes en uno de 64 bits). En un entorno de 64 bits que incluye todos los tipos de referencias y la mayoría de los tipos de valores integrados ( byte , short , int , long , etc.).

Establecer un valor mayor no es atómico y puede causar desgarros donde solo se actualiza parte de la memoria.

DateTime es una estructura que incluye un solo campo ulong contiene todos sus datos ( Ticks y DateTimeKind ) y ulong por sí mismo es atómico en un entorno de 64 bits.

¿ DateTime significa que DateTime es atómico? ¿O puede el siguiente código provocar desgarros en algún momento?

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


De la especificación del lenguaje C #.

5.5 Atomicidad de referencias variables Las lecturas y escrituras de los siguientes tipos de datos son atómicas: bool, char, byte, sbyte, short, ushort, uint, int, float y tipos de referencia . Además, las lecturas y escrituras de tipos de enumeración con un tipo subyacente en la lista anterior también son atómicas. No se garantiza que las lecturas y escrituras de otros tipos, incluidos los largos, ulong, dobles y decimales, así como los tipos definidos por el usuario, sean atómicos. Aparte de las funciones de biblioteca diseñadas para ese propósito, no hay garantía de lectura-modificación-escritura atómica, como en el caso de incremento o disminución.


De la sección de especificaciones de ECMA "I.12.6.6 Lecturas y escrituras atómicas"

Una CLI conforme garantizará que el acceso de lectura y escritura a ubicaciones de memoria correctamente alineadas no más grandes que el tamaño de la palabra nativa (el tamaño del tipo native int ) sea atómico (ver §I.12.6.2) cuando todos los accesos de escritura a una ubicación son el mismo tamaño. Las escrituras atómicas no alterarán más bits que los escritos. A menos que se use el control de diseño explícito (ver Partición II (Diseño de instancia de control)) para alterar el comportamiento predeterminado, los elementos de datos que no sean más grandes que el tamaño de palabra natural (el tamaño de un native int ) se alinearán correctamente. Las referencias a objetos se tratarán como si estuvieran almacenadas en el tamaño de palabra nativo.

Un native int es un IntPtr en C #.

Siempre que sizeof(IntPtr) >= sizeof(DateTime) sea ​​cierto para el entorno de tiempo de ejecución (también conocido como: se ejecuta como 64 bits), y no alteren la estructura interna para que sea un diseño explícito con bytes desalineados en lugar de [StructLayout(LayoutKind.Auto)] que tiene actualmente, luego lee y escribe una estructura DateTime (o cualquier otra estructura que siga esas reglas) está garantizada como atómica por la especificación ECMA.

Puede verificar eso ejecutando el siguiente código en un entorno de 64 bits:

public unsafe static void Main() { Console.WriteLine(sizeof(DateTime)); // Outputs 8 Console.WriteLine(sizeof(IntPtr)); // Outputs 8 Console.WriteLine(sizeof(ulong)); // Outputs 8 }