【问题标题】:Why does GCC's threading standard library implementation throw exceptions if you don't include pthread?如果不包含 pthread,为什么 GCC 的线程标准库实现会抛出异常?
【发布时间】:2020-06-10 16:37:32
【问题描述】:

当我编写使用例如std::promise 的代码并且我没有在 GCC 中包含 PThread 库时,我会抛出异常而不是链接器错误。例如:

void product(std::promise<int> intPromise, int a, int b)
{
    intPromise.set_value(a * b);
}
int main()
{
    int a = 20;
    int b = 10;
    std::promise<int> prodPromise;
    std::future<int> prodResult = prodPromise.get_future();
    product(std::move(prodPromise), a, b);
    std::cout << "20*10= " << prodResult.get() << std::endl;
}

如果我在没有-pthread 的情况下编译此代码,则会引发以下异常:

terminate called after throwing an instance of 'std::system_error'
  what():  Unknown error -1
Aborted (core dumped)

如果std::promise 在内部使用pthread 库,那么如果我没有将-pthread 命令行选项提供给g++,它应该会引发链接错误。但它的编译没有任何错误,并且在运行时我遇到了上述问题。

【问题讨论】:

  • @TedLyngmo:他在问(认为需要很长时间才能明白这一点)为什么它会抛出异常而不是完全链接失败。
  • libstdc++ 有一个可插入的线程实现 (gthreads)。可插拔性在运行时处理。
  • 好吧,我可以解释一下how,但我还是很高兴知道原因。
  • clang++ + libc++ works finelibstdc++ 中的错误?
  • @TedLyngmo,不是错误,而是实现细节。

标签: c++ pthreads c++14


【解决方案1】:

原因是libstdc++ 使用了所谓的weak references

我们可以轻松追踪您的特定代码示例引发异常的原因。 set_value() 致电 std::call_once。该函数在其实现中有the line*:

int e = gthread_once(&once.M_once, &once_proxy);

在哪里gthread_onceis

static inline int gthread_once(gthread_once_t *once, void (*func)(void))
{
  if (gthread_active_p())
    return ...
  else
    return -1;
}

gthread_active_p 返回false,这就是为什么gthread_once 返回-1,这在异常字符串中提到。

现在让我们take a lookgthread_active_p

static __typeof(pthread_key_create) gthrw_pthread_key_create
    __attribute__ ((weakref("__pthread_key_create")));

static inline int gthread_active_p(void)
{
  static void *const gthread_active_ptr = (void *)&gthrw_pthread_key_create;
  return gthread_active_ptr != 0;
}

gthrw_pthread_key_createweak reference__pthread_key_create。如果链接器没有找到符号__pthread_key_create,则&amp;gthrw_pthread_key_create 将是一个空指针,如果找到__pthread_key_create,则gthrw_pthread_key_create 将是它的别名。 __pthread_key_createpthreads 库导出。

标准库源码还有contains下面的注释:

对于一个多线程程序,它肯定必须使用的唯一东西是pthread_create。但是,可能有其他库使用自己的定义拦截 pthread_create 以出于某种目的包装 pthreads 功能。在这些情况下,pthread_create 被定义可能并不一定意味着 libpthread 实际被链接。

对于 GNU C 库,我们可以使用已知的内部名称。这在 ABI 中始终可用,但没有其他库会定义它。这是理想的,因为任何公共pthread 函数都可能像pthread_create might be 一样被拦截。 __pthread_key_create 是一个“内部”实现符号,但它是公共导出 ABI 的一部分。此外,每当使用pthread_create 时,静态libpthread.a 总是链接的符号之一,因此在任何静态链接的多线程程序中都不存在误报结果的危险。


* 一些下划线被删除,宏被扩展以提高可读性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-20
    • 1970-01-01
    • 1970-01-01
    • 2018-09-14
    相关资源
    最近更新 更多