Вы можете попробовать запустить его через CHESS , чтобы посмотреть, может ли оно вызвать чередование, которое нарушает тест.
Если вы посмотрите на дизассемблирование x86 (видимое из отладчика), вы также можете увидеть, генерирует ли джиттер инструкции, сохраняющие атомарность.
РЕДАКТИРОВАТЬ: я пошел дальше и запустил разборки (принудительно цель x86). Соответствующие строки:
double dCopy = _d;
00000039 fld qword ptr ds:[00511650h]
0000003f fstp qword ptr [ebp-40h]
_d = rand.Next(2) == 0 ? 0D : double.MaxValue;
00000054 mov ecx,dword ptr [ebp-3Ch]
00000057 mov edx,2
0000005c mov eax,dword ptr [ecx]
0000005e mov eax,dword ptr [eax+28h]
00000061 call dword ptr [eax+1Ch]
00000064 mov dword ptr [ebp-48h],eax
00000067 cmp dword ptr [ebp-48h],0
0000006b je 00000079
0000006d nop
0000006e fld qword ptr ds:[002423D8h]
00000074 fstp qword ptr [ebp-50h]
00000077 jmp 0000007E
00000079 fldz
0000007b fstp qword ptr [ebp-50h]
0000007e fld qword ptr [ebp-50h]
00000081 fstp qword ptr ds:[00159E78h]
Он использует один fstp qword ptr для выполнения операции записи в обоих случаях. Я предполагаю, что процессор Intel гарантирует атомарность этой операции, хотя я не нашел документации, подтверждающей это. Любой гуру x86, который может это подтвердить?
UPDATE:
Сбой, как и ожидалось, если вы используете Int64, который использует 32-разрядные регистры на процессоре x86, а не специальные регистры FPU. Вы можете увидеть это ниже:
Int64 dCopy = _d;
00000042 mov eax,dword ptr ds:[001A9E78h]
00000047 mov edx,dword ptr ds:[001A9E7Ch]
0000004d mov dword ptr [ebp-40h],eax
00000050 mov dword ptr [ebp-3Ch],edx
UPDATE:
Мне было любопытно, не получится ли это, если я принудительно выровняю не 8-байтовое выравнивание двойного поля в памяти, поэтому я собрал этот код:
[StructLayout(LayoutKind.Explicit)]
private struct Test
{
[FieldOffset(0)]
public double _d1;
[FieldOffset(4)]
public double _d2;
}
private static Test _test;
[STAThread]
static void Main()
{
new Thread(KeepMutating).Start();
KeepReading();
}
private static void KeepReading()
{
while (true)
{
double dummy = _test._d1;
double dCopy = _test._d2;
// In release: if (...) throw ...
Debug.Assert(dCopy == 0D || dCopy == double.MaxValue); // Never fails
}
}
private static void KeepMutating()
{
Random rand = new Random();
while (true)
{
_test._d2 = rand.Next(2) == 0 ? 0D : double.MaxValue;
}
}
Это не дает сбоя, и сгенерированные инструкции x86 по существу такие же, как и раньше:
double dummy = _test._d1;
0000003e mov eax,dword ptr ds:[03A75B20h]
00000043 fld qword ptr [eax+4]
00000046 fstp qword ptr [ebp-40h]
double dCopy = _test._d2;
00000049 mov eax,dword ptr ds:[03A75B20h]
0000004e fld qword ptr [eax+8]
00000051 fstp qword ptr [ebp-48h]
Я экспериментировал с заменой _d1 и _d2 для использования с dCopy / set, а также пробовал FieldOffset, равный 2. Все сгенерировали одинаковые базовые инструкции (с различными смещениями выше), и все они не потерпели неудачу через несколько секунд (вероятно, миллиарды попыток) , Учитывая эти результаты, я осторожно уверен, что, по крайней мере, процессоры Intel x86 обеспечивают атомарность операций двойной загрузки / хранения независимо от выравнивания.