En C #, définir une valeur sur une variable est atomique tant que sa taille est au plus native int
(c'est-à-dire 4 octets dans un environnement d'exécution 32 bits et 8 octets sur un environnement 64 bits). Dans un environnement 64 bits qui inclut tous les types de références et la plupart des types de valeurs intégrés (byte
, short
, int
, long
, etc.) .
La définition d'une valeur plus élevée n'est pas atomique et peut provoquer un déchirement là où seule une partie de la mémoire est mise à jour.
DateTime
est une structure qui ne comprend qu'un seul champ ulong
contenant toutes ses données (Ticks
et le DateTimeKind
) et ulong
seul est atomique dans un environnement 64 bits.
Est-ce à dire que DateTime
est également atomique? Ou le code suivant peut-il conduire à un déchirement à un moment donné?
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 section spécification ECMA "I.12.6.6 Atomic lit et écrit"
Une CLI conforme garantira que l'accès en lecture et en écriture à des emplacements de mémoire correctement alignés ne dépassant pas la taille du mot natif (la taille du type
native int
) Est atomique (voir §I.12.6.2) lorsque tous les accès en écriture à un emplacement sont de la même taille. Les écritures atomiques ne doivent pas modifier d'autres bits que ceux écrits. À moins qu'un contrôle explicite de la disposition (voir Partition II (Contrôle de la disposition des instances)) soit utilisé pour modifier le comportement par défaut, les éléments de données ne dépassant pas la taille naturelle du mot (la taille d'unnative int
) Doivent être correctement alignés. Les références d'objets doivent être traitées comme si elles étaient stockées dans la taille native de Word.
Un native int
Est un IntPtr
en C #.
Tant que sizeof(IntPtr) >= sizeof(DateTime)
est vrai pour l'environnement d'exécution (alias: fonctionnant en 64 bits), et qu'ils ne modifient pas la structure interne pour être une mise en page explicite avec des octets mal alignés au lieu de [StructLayout(LayoutKind.Auto)]
il possède actuellement, puis lit et écrit une structure DateTime
(ou toute autre structure qui suit ces règles) sont garanties atomiques par la spécification ECMA.
Vous pouvez le vérifier en exécutant le code suivant dans un environnement 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
}
Exécuter quelques tests et basé sur réponse ci-dessus il est assez sûr de dire qu'il est atomique aujourd'hui.
J'ai écrit un test pour vérifier combien de larmes ont pu être trouvées pendant les itérations X sur N threads pour Int64, DateTime et 3 structures personnalisées de 128, 192 et 256 tailles - aucune avec leur StructLayout foiré.
Le test consiste en:
Les résultats sont les suivants sur ma machine (Core i7-4500U, Windows 10 x64, .NET 4.6, version sans débogage, cible de la plate-forme: x64 avec optimisation de code):
-------------- 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 --------------------
Le code du test peut être trouvé ici: https://Gist.github.com/Flash3001/da5bd3ca800f674082dd8030ef70cf4e
De la spécification du langage C #.
5.5 Atomicité des références de variables Les lectures et écritures des types de données suivants sont atomiques: bool, char, byte, sbyte, short, ushort, uint, int, float et reference types. De plus, les lectures et écritures des types enum avec un type sous-jacent dans la liste précédente sont également atomiques. Les lectures et écritures d'autres types, y compris long, ulong, double et décimal, ainsi que les types définis par l'utilisateur, ne sont pas garantis pour être atomique. Hormis les fonctions de bibliothèque conçues à cet effet, il n'y a aucune garantie de lecture-modification-écriture atomique, comme dans le cas de l'incrémentation ou de la décrémentation.