频繁调用std::async是否可行?

4

我写了一个使用 std::async 进行并行处理的小程序,但它总是崩溃。我相信有更好的方法来实现这个功能,但现在我只想知道发生了什么。我不会发布确切的代码,因为我认为这并没有什么区别。它基本上看起来像这样:

while(1)
{
    std::vector<Things> things(256);

    auto update_the_things = [&](int start, int end) { //some code };

    auto handle1 = std::async(std::launch::async, update_the_things, 0, things.size() / 4);
    auto handle2 = std::async(std::launch::async, update_the_things, things.size() / 4, things.size() / 4 * 2);
    auto handle3 = std::async(std::launch::async, update_the_things, things.size() / 4 * 2, things.size() / 4 * 3);
    update_the_things(things.size() / 4 * 3, things.size());

    handle1.get();
    handle2.get();
    handle3.get();
}

这个循环每秒运行数千次,随机时间后(5秒-1分钟)就会崩溃。如果我在任务管理器中查看,我会发现该程序的线程计数正在快速波动,这让我想到std::async在每次调用时启动新线程。我本以为它会使用线程池之类的东西。无论如何,这是因为我做错了什么而导致崩溃吗?
使用GDB,我得到以下结果:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 3560.0x107c]
0x0000000000000000 in ?? ()

#0 0x0000000000000000 in ?? ()
#1 0x000000000041d18c in pthread_create_wrapper ()
#2 0x0000000000000000 in ?? ()

如您要求的那样,以下是gcc -v的输出:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=c:/tdm-gcc-64/bin/../libexec/gcc/x86_64-w64-mingw32/4.8.1/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-4.8.1/configure --build=x86_64-w64-mingw32 --enable-targets=all --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-libgomp --enable-lto --enable-graphite --enable-cxx-flags=-DWINPTHREAD_STATIC --enable-libstdcxx-debug --enable-threads=posix --enable-version-specific-runtime-libs --enable-fully-dynamic-string --enable-libstdcxx-threads --enable-libstdcxx-time --with-gnu-ld --disable-werror --disable-nls --disable-win32-registry --prefix=/mingw64tdm --with-local-prefix=/mingw64tdm --with-pkgversion=tdm64-2 --with-bugurl=http://tdm-gcc.tdragon.net/bugs
Thread model: posix
gcc version 4.8.1 (tdm64-2) 

2
“我本以为它可以使用线程池之类的东西来工作。” 这完全是实现定义的,你可能需要看看它在哪里崩溃才能回答你的问题。 - PeterT
@PeterT 我在std::async部分周围放置了一个try/catch,但什么也没捕获到。我只是得到一个“此程序已停止工作”的消息。我尝试使用gdb,但这会使程序变慢到无法触发崩溃的程度。它似乎只在循环运行特别快的时候才会发生。Gdb确实确认每个调用异步启动一个新线程。 - Chris_F
喔,你在Windows上使用gcc?是啊,我觉得gcc的实现目前不太周到(msvc可能会在Win8及以后版本中使用本地线程池机制)。程序崩溃时,在事件日志或其他地方没有显示出原因吗? - PeterT
@PeterT 问题中已添加信息。 - Chris_F
@Casey 是的,我也这么认为,问题是,这是stdlib中的错误还是由于winpthreads的不完整实现引起的? - PeterT
显示剩余8条评论
1个回答

5
这个符合标准的程序也会崩溃,而且通常速度更快:
#include <iostream>
#include <future>

int main() {
  try {
    for (;;) {
      std::async(std::launch::async, []{}).get();
    }
  } catch(...) { std::cout << "Something threw\n"; }
}

这是实现上的一个错误。


当您执行catch(std::exception &ex){std::cout << ex.what() << std::endl;}时,它是否显示了任何内容?因为如果您只是忘记将-pthread添加到编译标志中,则在某些gcc版本上也可能会发生这种情况。 - PeterT
没有解决方案吗? - liran63
顺便提一下,在Ubuntu Linux上,gcc 4.6.3和4.8.2都可以正常运行,而gcc的launch::async实现确实很原始,只是实例化了一个std::thread,因此这应该可以通过仅使用std::thread t([](){});t.join();而不是异步调用来重现。 - PeterT
为什么会崩溃,只是因为资源不足吗?这怎么成了一个 bug? - paulm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接