以下是我在工作站上尝试的代码。
class Program
{
public static volatile bool status = true;
public static void Main()
{
Thread FirstStart = new Thread(threadrun);
FirstStart.Start();
Thread.Sleep(200);
Thread thirdstart = new Thread(threadrun2);
thirdstart.Start();
Console.ReadLine();
}
static void threadrun()
{
while (status)
{
Console.WriteLine("Waiting..");
}
}
static void threadrun2()
{
status = false;
Console.WriteLine("the bool value is now made FALSE");
}
}
正如您所看到的,我在Main
中发了三个帖子。然后使用断点我跟踪线程。我的初始概念是所有三个线程将同时被触发,但我的断点流程显示线程执行流程一个接一个地跟随(输出格式也是如此,即从上到下执行线程)。伙计们为什么会这样?
另外,我尝试在声明中不使用volatile关键字运行相同的程序,我发现程序执行没有变化。我怀疑volatile
关键字没有实际用途。我在某个地方出错了吗?
答案 0 :(得分:4)
你的思维方法存在缺陷。
线程相关问题的本质是它们是非确定性的。这意味着您所观察到的可能不会指示将来会发生什么。
这就是为什么多线程编程“很难”的本质。它经常违反临时测试,甚至大多数单元测试。有效地实现它的唯一方法是了解整个软件和硬件堆栈,并通过使用状态机来绘制每个可能的事件。
总之,线程编程并不是关于你所见过的事情,而是关于可能发生的事情,无论多么不可能。
答案 1 :(得分:3)
好的,我会尝试尽可能简短地解释一个很长的故事:
Number 1 :尝试使用调试器检查线程的行为与重复运行多线程程序一样有用,并得出结论它工作正常,因为100次测试都没有失败:错误!线程表现为完全不确定(有些人会说是随机的)方式,你需要不同的方法来确保这样的程序能够正确运行。
数字2 :删除它后,volatile
的使用将变得清晰,然后在调试模式下运行程序,然后切换到发布模式。我想你会有一个惊喜......发布模式中发生的是编译器将优化代码(这包括重新排序指令和值的缓存)。现在,如果您的两个线程在不同的处理器核心上运行,那么执行正在检查status
值的线程的核心将缓存其值而不是反复检查它。另一个线程将设置它,但第一个将永远不会看到更改:死锁! volatile
可以防止出现这种情况。
从某种意义上说,volatile
是一个警卫,以防代码实际上(并且很可能不会)按照您认为在多线程场景中运行的方式运行。
答案 2 :(得分:2)
事实上,你的简单代码没有表现出与volatile不同的行为并不意味着什么。您的代码太简单,与volatile无关。您需要编写非常计算密集的代码来创建清晰可见的内存竞争条件。
此外,volatile
关键字在x86 / x64以外的其他平台上可能对其他内存模型有用。 (我的意思是像Itanium一样。)
Joe Duffy在他的博客上写了关于volatile
的有趣信息。我强烈建议您阅读。
答案 3 :(得分:0)
然后使用断点我跟踪了线程。我最初的构想 是所有的三个线程将同时被解雇,但我的 断点流表明线程执行流程遵循一个 在其他之后(输出格式也是如此,即从上到下执行 线程)。伙计们为什么会这样?
调试器暂时挂起线程以便于调试。
我怀疑volatile关键字没有实际的实际用途。我要去 某处错了?
Console.WriteLine
电话很可能是 fix 屏蔽问题。它们很可能隐含地为您生成必要的内存屏障。这是一段非常简单的代码片段,表明当volatile
未用于声明stop
变量时,实际上存在问题。
使用Release配置编译以下代码,并在调试器外部运行。
class Program
{
static bool stop = false;
public static void Main(string[] args)
{
var t = new Thread(() =>
{
Console.WriteLine("thread begin");
bool toggle = false;
while (!stop)
{
toggle = !toggle;
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
t.Join();
}
}