在32位MinGW 4.8.0中使用g ++时,带std :: thread的问题 [英] Issue with std::thread when using g++ in 32-bit MinGW 4.8.0

查看:326
本文介绍了在32位MinGW 4.8.0中使用g ++时,带std :: thread的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景 - 我们开发 C ++ 11 代码,并使用gtest / gmock编写单元测试。这是在Windows服务器上使用SCons和 g ++ MinGW 中构建的。我们在执行单元测试时开始偶尔出现问题:静默退出,期望错误,异常弹出窗口...没有明显的模式或共性,并且不容易重现。最终,一个同事把它缩小到一个情况,显然线程被加入,甚至没有开始执行其有效载荷功能。在这种情况下,没有例外或类似情况。测试简单失败,由于未达到期望。



简要问题 - 考虑下面的代码片段:

/ p>

  bool flag(false); 
std :: thread worker([&](){flag = true;});
worker.join();
assert(flag);

执行一次后,这看起来工作强>。 一次我的意思是在测试可执行文件。



然而,当在测试本身中重复执行时,上述断言通常失败;



出现g ++ std :: thread 在MinGW(4.8.0 / 32)下表现不佳 - 线程成功创建(即没有异常),它是可连接的,并且可以连接。然而,在某些情况下,其有效载荷功能从未执行。 - 我知道MinGW没有完整的POSIX线程,我已经查看了使用MinGW的线程? pthread_create没有足够的空间 MinGW和std :: thread ,并且都没有效果。我们使用静态链接(出于其他原因),我还发现 https:// gcc.gnu.org/bugzilla/show_bug.cgi?id=57740



这是线程实现中竞态条件的所有类型。在测试 volatile 和关闭优化( -O0 )两个布尔标志没有任何区别。



我们目前在 32位MinGW版本4.8.0 中使用 g ++ (开箱即用的QT5.1安装),现在考虑移动到一个不同的工具链(例如Linux盒子上的gcc / g ++),或者至少升级到以后的MinGW,如果有迹象表明这个问题可能已经修复。



这是一个在MinGW上的std :: thread的已知问题吗? 有任何修复或解决方法吗? / em>修复,我已经实现了一些case-by-case-work-arounds似乎工作,但我不喜欢他们。)



全详细 - 执行下面的代码,我们注意到:



[A] 测试#2 。 [预期]



[B] 但是,#4 ,包括只有两个(!)重复;虽然有时在测试失败之前需要数千个)。 [意外]



[C] 独家启用等待#1 会导致 > #4 条件下的故障(测试#2 仍然不会失败)。 [意外]



[D] 独家启用等待#3 #4 成功。 [Hmm ...]



用[D]fix和数千次重复后,我看到R6016(线程数据空间不足)。 (在某种程度上,这是可以理解的,也许不是那么令人担心,只要线程资源在测试和测试之间周期性地恢复不是背靠背运行。)



请注意,#1和#3的等待只是为了说明 - 他们没有超时,可能会挂起。

  #include< cassert> 
#include< cstdio>
#include< cstdlib>
#include< thread>

int main(int,char * [])
{
bool flag1(false);
assert(not flag1);

std :: thread worker1([&](){flag1 = true;});
assert(worker1.joinable());

// while(not flag1){std :: this_thread :: yield(); } //#1:MAKES#4 FAIL更多OFTEN

worker1.join();
if(not flag1)//#2:DOES NOT FAIL
{
puts(Oops on first!
exit(EXIT_FAILURE);
}

bool flag2(false);
assert(not flag2);

std :: thread worker2([&](){flag2 = true;});
assert(worker2.joinable());

// while(not flag2){std :: this_thread :: yield(); } //#3:MAKes#4 SUCCEED

worker2.join();
if(not flag2)//#4:SOMETIMES FAILS
{
puts(Oops on second!
exit(EXIT_FAILURE);
}

puts(Both OKAY);
return EXIT_SUCCESS;
}

编译到test.exe中,上述测试可以重复运行: / p>

  @ECHO OFF 
FOR / L %% i IN(1,1,1000000)DO(
ECHO __ %% i ________________________________________________________________________________ %% i __
test.exe
如果ERRORLEVEL 1 GOTO gameover

:gameover
pre>

EDIT




  • out,使用bool是不正确的。最初,我使用 atomic_bool ,具有与上述相同的行为。然后我错误地将示例简化为bool。

  • 无需检查#1和#3处的标志,只使用 yield 已足够。


解决方案

分析和伟大的例子!

我使用x86_64-w 64 -mingw32-g ++(GCC)4.8.2,
标志检查了此示例:
-c -pipe - fno-keep-inline-dllexport -m64 -g -frtti -Wall -Wextra -fexceptions -mthreads

下运行
Windows 7标志为-std = c ++ 0x

第二次(循环迭代293,805,1632,276)



Windows 7 with flag - std = c ++ 11

每次第二次循环迭代失败的次数较多(循环迭代4,257,613,49)



Windows 10标记为-std = c ++ 0x

第二个时间过长(循环迭代44924)失败。



Windows 10标记为-std = c ++ 11

长时间后失败(循环迭代7389 ,41907)第二。





未使用的优化。
在VirtualBox中执行的测试,在没有更新的Windows 7/10的干净安装。

测试可执行文件需要库:




  • libstdc ++ - 6.dll

  • libwinpthread-1.dll

  • libgcc_s_sjlj-1.dll



它在Windows 10下肯定更加稳定,但不是完美的。

在Windows7下使用c ++ 11而不是c ++ 0x可能不太稳定。但是我运行的测试太少,无法确定这一点。


有没有人尝试过较新版本的MinGW?


BACKGROUND -- We develop C++11 code and write unit tests using gtest/gmock. This is built on a Windows server using SCons and g++ in MinGW. We started having occasional problems when executing unit tests: silent exits, expectation errors, exception pop-ups... with no obvious pattern or commonality and not easily reproduceable. Eventually, a colleague narrowed it down to a case when apparently a thread was joined without even starting to execute its payload function. In this case, there were no exceptions or alike. The test simply failed due to expectation not being met. I then made a further simpler test case involving neither our codebase nor gtest/gmock.

BRIEF QUESTION -- Consider the following code snippet:

bool flag(false);
std::thread worker( [&] () { flag = true; } );
worker.join();
assert(flag);

When executed once, this appears to work fine. By "once" I mean once in the test executable. This executable is then run repeatedly many times from a command file.

However, when executed repeatedly within the test itself, the above assertion would often fail; sometimes on the very second repetition, other times after many thousands repetitions.

It appears g++ std::thread does not behave well under MinGW (4.8.0/32) -- Thread is successfully (i.e. no exceptions) created, it is joinable, and it can be joined. However, in some cases its payload function in never executed. -- I know MinGW does not have full POSIX pthreads and I already looked at Using threads with MinGW?, pthread_create not enough space, MinGW and std::thread, and alike to no avail. We do use static linking (for a different reason) and I also found https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57740.

It all kind of points to a race condition in thread implementation. Making both boolean flags in the test volatile and turning optimization off (-O0) made no difference.

We currently use g++ in 32-bit MinGW version 4.8.0 (out-of-the-box from QT5.1 installation) and are now considering move to a different toolchain (e.g. gcc/g++ on a Linux box) or at least upgrading to later MinGW if there is an indication that this problem might have been fixed.

Is this a known problem with std::thread on MinGW? Are there any fixes or work-arounds? (I mean general fixes. I already implemented some case-by-case- work-arounds that seem to work but I do not like them.)

FULL DETAILS -- Executing the code below we note that:

[A] Running the code below (so far) execution never fails test at #2. [Expected]

[B] However, test at #4 quite frequently fails (after different number of repetition, including just two(!) repetitions; though sometimes it takes thousands before the test fails). [Unexpected]

[C] Exclusively enabling wait at #1 results in more failures of condition at #4 (test at #2 still does not fail). [Unexpected]

[D] Exclusively enabling wait at #3 makes tests at both #2 and #4 succeed. [Hmm...]

With [D] "fix" and after many thousands repetition, I have seen (twice so far) dreaded R6016 (-not enough space for thread data). (In a way, this is understandable and perhaps not so worrying as long as thread resources are recovered periodically between tests and tests are not run back-to-back.)

Note that the "waits" at #1 and #3 are only to illustrate - they don't have time-out and could possibly hang.

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <thread>

int main(int, char *[])
{
   bool flag1(false);
   assert(not flag1);

   std::thread worker1( [&] () { flag1 = true; } );
   assert(worker1.joinable());

// while (not flag1) { std::this_thread::yield(); } // #1: MAKES #4 FAIL MORE OFTEN

   worker1.join();
   if (not flag1) // #2: DOES NOT FAIL
   {
      puts("Oops on first!");
      exit(EXIT_FAILURE);
   }

   bool flag2(false);
   assert(not flag2);

   std::thread worker2( [&] () { flag2 = true; } );
   assert(worker2.joinable());

// while (not flag2) { std::this_thread::yield(); } // #3: MAKES #4 SUCCEED

   worker2.join();
   if (not flag2) // #4: SOMETIMES FAILS
   {
      puts("Oops on second!");
      exit(EXIT_FAILURE);
   }

   puts("Both OKAY");
   return EXIT_SUCCESS;
}

Compiled into test.exe, the above test can be run repeatedly using:

@ECHO OFF
FOR /L %%i IN (1,1,1000000) DO (
   ECHO __ %%i ________________________________________________________________________________ %%i __
   test.exe
   IF ERRORLEVEL 1 GOTO gameover
)
:gameover

EDIT

  • As @TC pointed out, using bool is not correct. Originally, I used atomic_bool with the same behavior as described above. I then wrongly "simplified" the example to bool.
  • BTW using only yield without checking the flag at #1 and #3 is not sufficient.

解决方案

Thanks a lot for the very detailed analysis and for the great example!
I've checked this example with x86_64-w64-mingw32-g++ (GCC) 4.8.2,
flags: -c -pipe -fno-keep-inline-dllexport -m64 -g -frtti -Wall -Wextra -fexceptions -mthreads
running under
Windows 7 with flag -std=c++0x
It failed rather early each time on second (loop iteration 293, 805, 1632, 276)

Windows 7 with flag -std=c++11
It failed rather early each time on second (loop iteration 4, 257, 613, 49)

Windows 10 with flag -std=c++0x
It failed after a long time (loop iteration 44924) on second.

Windows 10 with flag -std=c++11
It failed after a long time (loop iteration 7389, 41907) on second.


No optimizations where used. The tests where done in a VirtualBox with clean installations of Windows 7/10 without updates.
The test executables required the libraries:

  • libstdc++-6.dll
  • libwinpthread-1.dll
  • libgcc_s_sjlj-1.dll

So it is definitely much more stable under Windows 10, but not flawless.
Using c++11 instead of c++0x might be less stable under Windows7. But I've run too few tests to be certain about that.

Has anybody tried with a newer version of MinGW?

这篇关于在32位MinGW 4.8.0中使用g ++时,带std :: thread的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆