Бесконечная петля в режиме деблокирования
Когда я запускаю следующий код в режиме отладки, он будет успешно завершен и завершен. Однако, если я запустил следующий код в режиме выпуска, он застрянет в бесконечном цикле и никогда не закончится.
static void Main(string[] args)
{
bool stop = false;
new Thread(() =>
{
Thread.Sleep(1000);
stop = true;
Console.WriteLine("Set \"stop\" to true.");
}).Start();
Console.WriteLine("Entering loop.");
while (!stop)
{
}
Console.WriteLine("Done.");
}
Какая оптимизация заставляет его застревать в бесконечном цикле?
Ответы
Ответ 1
Мое предположение было бы кешированием процессора переменной stop
в основном потоке. В режиме отладки модель памяти более строгая, потому что отладчик должен иметь возможность обеспечить разумное представление состояния переменной во всех потоках.
Попробуйте создать поле и отметьте его как volatile:
volatile bool stop = false;
static void Main(string[] args)
{
new Thread(() =>
{
Thread.Sleep(1000);
stop = true;
Console.WriteLine("Set \"stop\" to true.");
}).Start();
Console.WriteLine("Entering loop.");
while (!stop)
{
}
Console.WriteLine("Done.");
}
Ответ 2
Поскольку он не является потокобезопасным, вы обновляете переменную основного потока stop
внутри дочернего потока. Это всегда будет непредсказуемо. Чтобы работать с любой ситуацией, как это, посмотрите эту статью.
Ключевое слово volatile инструктирует компилятор генерировать приобретать ограждение при каждом чтении с этого поля, а также каждая запись в это поле. Забор-ограждение предотвращает другие считывает/записывает из перемещения перед забором; забор предотвращает перемещение других прочтений/записей после забора. Эти "полузащитники" быстрее, чем полные заборы, потому что они дают время выполнения и аппаратное обеспечение больше возможностей для оптимизации.
Ответ 3
Тема Небезопасный код непредсказуем. Основная проблема заключается в изменении одной переменной потока из другого потока. Сделайте переменную глобальной или изменчивой. Вы можете сделать это, следуя
static volatile bool stop = false;
static void Main(string[] args)
{
new Thread(() =>
{
Thread.Sleep(1000);
stop = true;
Console.WriteLine("Set \"stop\" to true.");
}).Start();
Console.WriteLine("Entering loop.");
while (!stop)
{
}
Console.WriteLine("Done.");
}
Ответ 4
Похоже, что какая-то оптимизация для значения локальной переменной - изменение поля приводит к завершению ok (обратите внимание, что в реальном коде следует использовать летучую или правильную блокировку):
using System;
using System.Threading;
class Program
{
static bool stop = false;
static void Main(string[] args)
{
new Thread(() =>
{
Thread.Sleep(1000);
stop = true;
Console.WriteLine("Set \"stop\" to true.");
}).Start();
Console.WriteLine("Entering loop.");
while (!stop)
{
}
Console.WriteLine("Done.");
}
}