【问题标题】:Reason for unknown exception in promise without lpthread没有 lpthread 的 Promise 中出现未知异常的原因
【发布时间】:2020-02-24 17:45:13
【问题描述】:

请注意,这个问题在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 有关。很好奇为什么这表现为运行时错误而不是链接时间。

【问题讨论】:

  • 你应该编译和链接-pthread,而不是-lpthread (stackoverflow.com/questions/2127797/…)
  • @Mat -pthread 也可以。但问题是专门询问为什么我们在运行时而不是链接时收到此错误。

标签: c++ c++11


【解决方案1】:

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-29
  • 1970-01-01
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
  • 2012-11-12
  • 1970-01-01
相关资源
最近更新 更多