在发布配置上使用多线程的Visual Studio C ++运行时问题

时间:2016-09-30 23:32:09

标签: c++ multithreading visual-studio-2015

当我使用配置设置为release(对于x86和x64)进行编译时,我的程序无法完成。为了澄清,没有构建错误或执行错误。

在查找问题的原因和解决方案后,我发现Program only crashes as release build -- how to debug?提出这是一个数组问题。虽然这解决了我的问题,但它给了我一些关于此事的见解(我将其留给下一个人)。

为了进一步混淆,只有当主线程上的子程序的执行时间大于约0ms时才会出现问题。

以下是相关的代码部分:

//  Startup Progress Bar Thread
nPC_Current = 0; // global int
nPC_Max = nPC; // global int (max value nPC_Current will reach)

DWORD myThreadID;
HANDLE progressBarHandle = CreateThread(0, 0, printProgress, &nPC_Current, 0, &myThreadID);

/* Do stuff and time how long it takes (this is what increments nPC_Current) */

//  Wait for Progress Bar Thread to Terminate
WaitForSingleObject(progressBarHandle, INFINITE);

我的程序被困在哪一行是最后一个语句,程序等待创建的线程终止:

WaitForSingleObject(progressBarHandle, INFINITE);

以下是进度条功能的代码:

DWORD WINAPI printProgress(LPVOID lpParameter)
{   
    int lastProgressPercent = -1;   //  Only reprint bar when there is a change to display.

    //  Core Progress Bar Loop
    while (nPC_Current <= nPC_Max)
    {   
        // Do stuff to print a text progress bar
    }
    return 0;
}

核心&#39;如果测量子程序的执行时间约为0ms,则while循环通常不会获得单次迭代。为了澄清这一点,如果定时子程序的执行时间约为0ms,则在printProgressBar执行一次之前,nPC_Current将大于nPC_Max。这意味着线程将在主线程开始等待之前终止。

如果有人愿意帮助解决这个问题,或者就此事提供一些进一步的见解,那就太棒了,因为我在解决这个问题时遇到了一些麻烦。

谢谢!

编辑:

  • 措词
  • 删除了令人分心的内容并添加了说明

3 个答案:

答案 0 :(得分:6)

我的猜测是您忘记声明共享的全局变量volatilenPC_Current具体而言)。由于线程函数本身从不修改nPC_Current,因此在代码的发行版中,编译器优化了进度条循环到无限循环,永不改变nPC_Current的值。

这就是为什么您的进度条永远不会从代码的发布版本中的0%值更新,这就是您的进度条线程永远不会终止的原因。

P.S。此外,您最初打算将nPC_Current计数器作为线程参数传递给线程函数(通过CreateThread调用判断)。但是,在线程函数中,您忽略该参数并直接访问nPC_Current作为全局变量。坚持传递和访问它作为线程参数的原始想法可能是一个更好的主意。

答案 1 :(得分:1)

编写软件的首要原则是:

不遗余力;检查每一个可能的错误,无处不在。

注意:这是排除软件故障时的头号规则 ;当遇到麻烦时,已经太晚了;这是编写软件时的头号规则,也就是说,甚至在需要进行故障排除之前。

您的代码存在许多问题;我无法确定其中任何一个是导致你遇到问题的原因,但我愿意打赌,如果你修复了这些问题,并且如果你养成了解决这些问题的心态,那么你会没有你遇到的问题。

  1. WaitForSingleObject的文档说:“如果在等待仍处于暂挂状态时关闭此句柄,则该函数的行为未定义。”但是,您似乎没有断言CreateThread()返回了有效句柄。你甚至没有告诉我们你在哪里以及如何关闭这个句柄。 (当你关闭句柄时,你是否断言CloseHandle()没有失败?)

  2. 不仅你正在使用全局变量(这是我强烈反对的建议),而且你很乐意对它们的值进行多种假设,而不会断言任何一种假设。 / p>

    • 你有什么保证nPC_Current实际上比你函数开头的nPC_Max小?

    • 你有什么保证nPC_Current会随着时间的推移而不断增加?

    • 您有什么保证lastProgressPercent的计算实际上在循环期间不会让-1屈服?

    • 您有什么保证nPC_Max 为零? (在单独的线程上将零划分是很难捕捉的。)

    • 你有什么保证nPC_Max在你的线程运行时不会被修改?

    • 你有什么保证nPC_Current以原子方式递增? (我希望你明白,如果它没有原子地增加,那么当你从另一个线程读取它时,你可能会读垃圾。)

  3. 您已使用[C++]标记了此问题,我确实看到了一些正在使用的C ++功能,但我并没有真正看到任何面向对象的编程。线程函数精确地接受LPVOID参数,以便您可以将对象传递给它,从而继续在第二个线程中面向对象,具有所需的所有好处,例如封装。我建议你使用它。

答案 2 :(得分:-1)

您可以在发布中使用(有一些限制)断点...

代码的这一部分是:

/* Do stuff and time how long it takes (this is what increments nPC_Current) */

取决于printProgress线程的作用? (如果是这样,你必须确保时间依赖性,并且方便地订购)你确定这总是递增nPC_Current吗?它是时间依赖算法吗? 您是否测试了Sleep()在此处的效果?