如果没有语句,循环不检查条件

时间:2013-12-26 21:58:10

标签: c++ multithreading while-loop

这不应该太难理解:

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线程等待Thread1ThreadIsRunning设置为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变为假时没有任何反应

5 个答案:

答案 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