构造std::thread对象过程详解

Lin*_*gxi 6 c++ multithreading language-lawyer c++11 stdthread

我对构造std::thread对象的细节很感兴趣(并对此感到困惑)。根据cppreference,线程函数和所有参数都被值复制到一些线程可访问的存储中,然后调用。

1)这个线程可访问的存储究竟是什么?是不是语义上等同于某种线程本地存储,在线程函数返回后销毁变量?

2) 传递给线程函数时参数的值类别是什么?cppreference 上的描述表明它们作为左值传递(无论如何它们都被赋予了名称)。我对 GCC 和 clang 的测试似乎表明相反,即 r 值。具体来说,以下代码无法编译:

void f(int& a) {
  std::cout << ++a << '\n';
}

int main() {
    std::thread t(&f, 1);
    t.join();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我们f改为

void f(int&& a) {
  std::cout << ++a << '\n';
}

int main() {
    std::thread t(&f, 1);
    t.join();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

那么,标准对此有何看法?

Rei*_*ica 3

1) 这个“线程可访问存储”文本位在标准中没有直接表示。该标准只是简单地说该函数是用 获得的参数调用的decay_copy

\n\n

2)如果你decay_copy仔细研究,你会发现它是按值返回的(因为它的返回类型是std::decaysomething)。因此,该函数f是使用右值参数(实际上是纯右值参数)调用的。

\n\n

如果要传递左值(引用),可以使用std::refstd::cref来包装它们。

\n\n

确切的引用,C++11 30.3.1.2/4:

\n\n
\n

作用:构造一个类型为 的对象thread。新的执行线程通过在构造线程中评估INVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)对\n 的调用来 执行。DECAY_COPY此调用的任何返回值都将被忽略。[注意:这意味着调用 \n 副本时未抛出的任何异常都f将在构造线程中抛出,而不是在新线程中抛出。\xe2\x80\x94end note ] 如果\n 的调用 INVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)因未捕获的异常而终止\n,std::terminate则应调用。

\n
\n\n

DECAY_COPY30.2.6/1 中定义:

\n\n
\n

本条款中的多个地方DECAY_COPY(x)使用了该操作。所有这些使用都意味着调用函数 decay_copy(x)并使用结果,其中decay_copy定义如下:

\n\n
template <class T> typename decay<T>::type decay_copy(T&& v)\n{ return std::forward<T>(v); }\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n

INVOKE在 20.8.2 中定义的方式与 cppreference 在您提供的链接中描述调用的方式几乎相同。

\n