Попробуйте переписать это так:
public void Start()
{
var thread = new Thread(() =>
{
Thread.Sleep(5000);
stop = true;
});
thread.Start();
bool unused = false;
while (stop == false)
unused = !unused; // fake work to prevent optimization
}
И убедитесь, что вы работаете в режиме Release, а не в режиме Debug. В режиме Release применяются оптимизации, которые фактически приводят к сбою кода при отсутствии volatile
.
Редактировать : Немного о volatile
:
Все мы знаем, что в жизненный цикл программы вовлечены две различные сущности, которые могут применять оптимизации в форме кэширования переменных и / или переупорядочения команд: компилятор и ЦП.
Это означает, что может быть даже большая разница между тем, как вы написали свой код, и тем, как он действительно выполняется, поскольку инструкции могут быть переупорядочены относительно друг друга, или чтения могут быть кэшированы в том, что компилятор воспринимает как " улучшение скорости ".
В большинстве случаев это хорошо, но иногда (особенно в контексте многопоточности) это может вызвать проблемы, как показано в этом примере. Чтобы позволить программисту вручную предотвращать такую оптимизацию, были введены ограждения памяти, которые представляют собой специальные инструкции, роль которых состоит в том, чтобы предотвращать как переупорядочение команд (просто чтение, просто запись или оба) в отношении самого ограждения, так и принудительную отмену значений в кешах ЦП, таких, что их нужно перечитывать каждый раз (что мы и хотим в приведенном выше сценарии).
Несмотря на то, что вы можете указать полный забор, воздействующий на все переменные, через Thread.MemoryBarrier()
, это почти всегда излишнее, если вам нужно воздействовать только на одну переменную. Таким образом, для того, чтобы одна переменная всегда была актуальна для всех потоков, вы можете использовать volatile
для введения ограждений для чтения / записи только для этой переменной.