Считывая этот вопрос, я хотел проверить, могу ли я продемонстрировать неатоматичность чтения и записи по типу, для которого атомарность таких операций не гарантируется.
private static double _d;
[STAThread]
static void Main()
{
new Thread(KeepMutating).Start();
KeepReading();
}
private static void KeepReading()
{
while (true)
{
double dCopy = _d;
// In release: if (...) throw ...
Debug.Assert(dCopy == 0D || dCopy == double.MaxValue); // Never fails
}
}
private static void KeepMutating()
{
Random rand = new Random();
while (true)
{
_d = rand.Next(2) == 0 ? 0D : double.MaxValue;
}
}
К моему удивлению, утверждение отказалось провалиться даже после трех минут исполнения. Что дает?
- Тест неверен.
- Специфические временные характеристики теста делают маловероятным/невозможным то, что утверждение не получится.
- Вероятность настолько низкая, что я должен запустить тест намного дольше, чтобы он мог вызвать его.
- CLR обеспечивает более надежные гарантии атомарности, чем спецификация С#.
- Моя ОС/оборудование обеспечивает более надежные гарантии, чем CLR.
- Что-то еще?
Конечно, я не намерен полагаться на какое-либо поведение, которое явно не гарантируется спецификацией, но я хотел бы более глубокое понимание проблемы.
FYI, я запускал это как в отчетах Debug, так и Release (изменение Debug.Assert
до if(..) throw
) в двух отдельных средах:
- Windows 7 64-бит +.NET 3.5 SP1
- Windows XP 32-bit +.NET 2.0
EDIT: чтобы исключить возможность комментария Джона Кугельмана "отладчик не является безопасным для Schrodinger", я добавил строку someList.Add(dCopy);
к методу KeepReading
и проверил, что в этом списке не было ни одного устаревшего значение из кеша.
EDIT:
Основываясь на предложении Дэна Брайанта: Использование long
вместо double
прерывает его практически мгновенно.