这不应该太难理解:
bool ThreadIsRunning = false;
void Thread1(void *nothing){
ThreadIsRunning = true;
cout << "The thread is running!\n";
Sleep(1000);
cout << "Ending thread!\n");
ThreadIsRunning = false;
_endthread();
return;
}
int main(){
std::cout << "The thread is starting!\n";
_beginthread(Thread1, 0, 0);
std::cout << "Waiting for thread to end!\n";
while(ThreadIsRunning);
std::cout << "The thread is ended!\n";
return 0;
}
main
线程等待Thread1
将ThreadIsRunning
设置为false,对吗?
如果我放while(ThreadIsRunning) Sleep(10);
,它会起作用,但我认为我的代码无需工作。
while(ThreadIsRunning) void();
也不起作用。
我正在使用Visual Studio Ultimate 2012。
C / C ++命令行选项:
/GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"Release\vc110.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\" /EHsc /nologo /Fo"Release\" /Fp"Release\Test1.pch"
链接器命令行选项:
/OUT:"<projectsfolder>\Test1\Release\Test1.exe" /MANIFEST /LTCG /NXCOMPAT /PDB:"<projectsfolder>\Test1\Release\Test1.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /MACHINE:X86 /OPT:REF /SAFESEH /INCREMENTAL:NO /PGD:"<projectsfolder>\Test1\Release\Test1.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"Release\Test1.exe.intermediate.manifest" /OPT:ICF /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
修改
有一个 NOT 时间表/比赛/时间问题。即使我在Sleep(1000);
之后添加_beginthread(Thread1, 0, 0)
,问题仍然是当ThreadIsRunning
变为假时没有任何反应
答案 0 :(得分:4)
这可以归结为线程调度 - 完全有可能在线程开始之前,运行main()
的线程到达你的while()语句并在线程有机会改变之前突破它你的全局布尔值为true
。这可能就是为什么你看到添加Sleep
调用让线程有机会从false变为true,你会看到你期望的行为。
答案 1 :(得分:2)
C ++ 03不是一种线程感知语言,优化器可以清楚地看到&#34; (不知道另一个线程可能正在改变它)ThreadIsRunning
在循环体中没有改变。一旦将调用添加到sleep
,编译器就必须假设sleep访问ThreadIsRunning
的别名副本,并且必须在循环中每次迭代检查其值。
你的问题的解决方案是不使用标准变量和等待循环(它将占用CPU核心)。而是在适当的时候使用条件变量和线程之间的信号,因为这是在线程之间传递这种信息的标准方法。
答案 2 :(得分:0)
您可能必须将ThreadIsRunning声明为volatile。编译器可能已对其进行了优化,因为它没有在main()中看到任何修改它的内容。
答案 3 :(得分:0)
在开始主题之前添加ThreadIsRunning = true;
:
std::cout << "The thread is starting!\n";
ThreadIsRunning = true; // <============= add this
_beginthread(Thread1, 0, 0);
// etc.
这会告诉你它是否是一个调度问题(很可能)。
答案 4 :(得分:0)
问题是编译器可以自由地将变量ThreadIsRunning
的负载提升出循环 - 在循环之前将其加载到寄存器中然后检查寄存器(而不是变量)循环时间。寄存器永远不会变错,所以循环永远不会退出。
最简单,最明显的解决方法是标记变量volatile
。这样,编译器知道它不能支持负载,并且每次都需要在循环周围重新加载变量。
使事情有效的另一个“修复”是在循环中调用任何外部定义的函数(例如Sleep
)。由于编译器不知道Sleep
做了什么,因此必须假设它可能会更改全局变量,因此需要重新加载。调用本地函数(之前在同一文件或标题中定义的函数)可能不起作用,因为编译器知道函数的作用(特别是它不会修改ThreadIsRunning
)