请注意,这个问题在herehere处都有一个“答案”,但我的问题不是关于如何消除错误,而是为什么会发生此错误。

考虑以下代码:

include <cstdio>
#include <future>

int main() {
  std::promise<int> promise;
  auto future = promise.get_future();
  promise.set_value(42);

  auto result = future.get();
  printf("%d\n", result);
}

此代码引发异常:
$ g++ -g -std=c++1z main.cpp
$ ./a.out
terminate called after throwing an instance of 'std::system_error'
  what():  Unknown error -1
Aborted (core dumped)

解决方案是在命令行上传递-lpthread:
$ g++ -g -std=c++1z -lpthread main.cpp
$ ./a.out
42

现在,当我不链接所需的库时,我经常会遇到链接错误。这是我第一次遇到运行时错误。

当您在gdb下运行版本(不使用lpthread)时,这是您将获得的堆栈跟踪(其中部分已删除):
#5  0x00007ffff7aa6ef3 in __cxxabiv1::__cxa_throw (obj=obj@entry=0x61ad00,
    tinfo=tinfo@entry=0x7ffff7dce6b0 <typeinfo for std::system_error>,
    dest=dest@entry=0x7ffff7ad02b0 <std::system_error::~system_error()>)
    at /tmp/tmp.kmkSDUDFn8/build/../gcc-9.1.0/libstdc++-v3/libsupc++/eh_throw.cc:95

#6  0x00007ffff7a9d0ec in std::__throw_system_error (__i=-1)
    at /tmp/tmp.kmkSDUDFn8/build/x86_64-pc-linux-gnu/libstdc++-v3/include/ext/new_allocator.h:89

#7  0x000000000040240f in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (__once=..., __f=
    @0x7fffffffdd80: (void (std::__future_base::_State_baseV2::*)(class std::__future_base::_State_baseV2 * const, class std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>()> *, bool *)) 0x40200c <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __args#0=@0x7fffffffdd78: 0x61ac30, __args#1=@0x7fffffffdd70: 0x7fffffffddf0,
    __args#2=@0x7fffffffdd68: 0x7fffffffdd67) at /.../include/c++/9.1.0/mutex:697

#8  0x0000000000401e5d in std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) (this=0x61ac30, __res=..., __ignore_failure=false)
    at /.../include/c++/9.1.0/future:401

因此,它与call_once有关。好奇为什么这表现为运行时错误而不是链接时间。

最佳答案

GNU libc提供了Pthread函数的替代实现,因此即使没有-pthread,链接也可以成功。参见is pthread in glibc.so implemented by weak symbol to provide pthread stub functions?Why glibc and pthread library both defined same APIs?

GNU C++标准 std::call_once 可以

int __e = __gthread_once(&__once._M_once, &__once_proxy);

__gthread_once 是:
static inline int
__gthread_once (__gthread_once_t *__once, void (*__func) (void))
{
  if (__gthread_active_p ())
    return __gthrw_(pthread_once) (__once, __func);
  else
    return -1;
}

GNU C++标准库在运行时检测 pthread 函数中是否可用__gthread_active_p库实现。
/* For a program to be multi-threaded the only thing that it certainly must
   be using is pthread_create.  However, there may be other libraries that
   intercept pthread_create with their own definitions to wrap pthreads
   functionality for some purpose.  In those cases, pthread_create being
   defined might not necessarily mean that libpthread is actually linked
   in.
   For the GNU C library, we can use a known internal name.  This is always
   available in the ABI, but no other library would define it.  That is
   ideal, since any public pthread function might be intercepted just as
   pthread_create might be.  __pthread_key_create is an "internal"
   implementation symbol, but it is part of the public exported ABI.  Also,
   it's among the symbols that the static libpthread.a always links in
   whenever pthread_create is used, so there is no danger of a false
   negative result in any statically-linked, multi-threaded program.
   For others, we choose pthread_cancel as a function that seems unlikely
   to be redefined by an interceptor library.  The bionic (Android) C
   library does not provide pthread_cancel, so we do use pthread_create
   there (and interceptor libraries lose).  */

作为引用答案的状态,在编译和链接多线程程序时都应该使用-pthread选项。 -lpthread不够,因为它没有定义某些代码可能需要的_REENTRANT宏:
$ diff <(g++ -E -dD -xc++ /dev/null) <(g++ -E -dD -xc++ -pthread /dev/null)
289a290
> #define _REENTRANT 1

关于c++ - 没有lpthread的Promise中出现未知异常的原因,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60381333/

10-13 03:32