Здравствуйте, VladD2, Вы писали:
VD>Дотнет не гарантирует атомарности и корректного разделение переменных и полей, но поле типа bool и целые, на практике, работают атомарно и корректно.
Дотнет
гарантирует атомарность всех примитивных типов у которых sizeof(T) <= IntPtr.Size. Это прописано в спецификации .NET Memory Model.
VD>Проблемы могут начаться, если нужна точная и быстрая реакция на изменение значения. Какое-то время разные процессоры будут иметь свои копии. И изменение переменной в одном потоке не сразу попадет в кэш другого. Но в твоем примере это не важно. Ну, выйдет второй поток не через 5 секунд, а через 5 целых и 1 сотую секунды.
Для x86 синхронизация между кешами данных происходит при: 1) Memory Barrier 2) автоматически каждые 10 нс
Не все архитектуры CPU делают автоматическую синхронизацию кешей данных. Поэтому правильное решение состоит в указании ключевого слова volatile для переменной.
Но поскольку указать volatile для переменной обьявленной "локально" нельзя с точки зрения синтаксиса, нужно использовать методы Volatile.Read и Volatile.Write.
Сначала продемонстрирую вырожденный случай:
static void Main()
{
bool b = true;
new Thread(
() =>
{
Thread.Sleep(1000);
b = false;
})
{
IsBackground = true
}
.Start();
while (b)
{
//Console.WriteLine("b=" + b);
//Thread.Sleep(100);
}
Console.WriteLine("b=" + b);
}
Этот пример зависает
навсегда если собрать его в Release конфигурации под .NET 5.0.
Обратите внимание, что я закоментировал Console.WriteLine и Thread.Sleep. Почему? Потому что внутри эти операции используют примитивы синхронизации ОС, а они подразумевают неявный memory barrier. Например, документация Windows это декларирует явно.
Теперь заставим этот пример работать как надо. Например, так:
static void Main()
{
bool b = true;
new Thread(
() =>
{
Thread.Sleep(1000);
Volatile.Write(ref b, false);
})
{
IsBackground = true
}
.Start();
while (Volatile.Read(ref b))
{
//Console.WriteLine("b=" + b);
//Thread.Sleep(100);
}
Console.WriteLine("b=" + b);
}
Или так:
static void Main()
{
bool b = true;
new Thread(
() =>
{
Thread.Sleep(1000);
Thread.MemoryBarrier();
b = false;
})
{
IsBackground = true
}
.Start();
while (b)
{
Thread.MemoryBarrier();
//Console.WriteLine("b=" + b);
//Thread.Sleep(100);
}
Console.WriteLine("b=" + b);
}