2012-05-25 5 views
19

Когда я запустил следующий код в debug режиме, он успешно завершит и выйдет. Однако, если я запустил следующий код в релиз, он застрянет в бесконечном цикле и никогда не закончится.Бесконечная петля в режиме освобождения

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."); 
} 

Какая оптимизация заставляет застревать в бесконечном цикле?

+0

Не нужно ли синхронизировать доступ к «остановке» между потоками? –

+0

Это вызывает некоторые вещи оптимизации. Компиляция создаст истинную установку остановки. – rekire

+0

Взгляните на новые классы CancellationToken. Они были изобретены для решения этой проблемы. http://msdn.microsoft.com/en-us/library/dd997364.aspx –

ответ

18

Мое предположение было бы кешированием процессора переменной 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."); 
} 
+0

Волатильность хороша для правильности, но для избежания оптимизации это похоже на то, что достаточно сделать поле вместо локальной переменной. –

+0

@AlexeiLevenkov Я мог ошибаться, но я считаю, что это деталь реализации CLR, и теоретически (хотя, вероятно, не практически) может быть изменена. –

+0

Интересно, почему вы не синхронизируете доступ к переменной. Я всегда это делаю, так как я предполагаю, что это не атомная операция, чтобы получить/установить переменную 'bool'. Неужели я ошибаюсь? –

11

Поскольку он не является потокобезопасным, вы обновляете переменную основного потока stop внутри дочерней нити. Это всегда будет непредсказуемо. Чтобы работать с такой ситуацией, посмотрите на this article.

Летучие ключевое слово инструктирует компилятор генерировать -забор сбора по на каждом чтении из этой области, и релиз-забор на каждый запись в этой области. Забор-ограждение предотвращает перемещение других записей перед забором; забор предотвращает перемещение других прочтений/записей после забора. Эти «полузащитники» быстрее, чем полные заборы, потому что они дают время выполнения и аппаратные средства для оптимизации.

0

Thread небезопасный код непредсказуемо. Основная проблема заключается в изменении одной переменной потока из другого потока. Сделайте переменную глобальной или изменчивой. Вы можете сделать это, следуя

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."); 
} 
0

Похоже, какой-то оптимизации для значения локальной переменной - изменение в поле сделать его прекратить нормально (обратите внимание, что летучее или правильный замок должен быть использован в настоящем коде):

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."); 
    } 
}