【问题标题】:std::thread arguments are copied on the new thread?std::thread 参数被复制到新线程上?
【发布时间】:2021-04-07 15:23:58
【问题描述】:

我已经开始阅读 Anthony Williams 的“C++ Concurrency in action,第二版”。在其 2.2 部分中,他写道,线程函数的参数被复制到新线程上。我在几个编译器上检查了这一点,这是真的:如果我们传递一个左值 - 它会被复制到新线程上。

Anthony 写道,这可能表示未定义的行为 - 左值可能会超出原始范围的范围,而新线程会启动。

这有点吓人。这是否意味着每个 std::thread 构造函数调用都有潜在的竞争?为什么此复制在从 std::thread 构造函数返回之前不同步?为什么它没有完全复制到原始线程上?

【问题讨论】:

  • 复制在“主”线程中完成,并且有同步。 (见en.cppreference.com/w/cpp/thread/thread/thread
  • @Mat:你能有时间仔细看看吗?正如我检查过的(请参阅我的答案),隐式转换是在新创建的线程内完成的,这不是我通过阅读 cppreference 上的文本所期望的。阅读 cppreference 是否有一些误解? Anthony 似乎是对的,因为我的示例显示的结果与 Anthony 解释的结果相同。

标签: c++ multithreading c++11 stl std


【解决方案1】:

发件人:std::thread::thread

除了对 decay_copy 的调用是在调用者的上下文中评估的,因此在评估和复制/移动参数期间抛出的任何异常都将在当前线程中抛出,而无需启动新线程。 构造函数调用的完成与(在 std::memory_order 中定义的)在新的执行线程上调用 f 的副本的开始同步。

这说明所有的复制动作都是在线程开始执行之前完成的。

但让我们测试一下!

如果我们运行这个简短的例子:

class CopyMe
{
    private:
        std::string s;
    public:
        CopyMe() = default;
        CopyMe( const CopyMe& ) { std::cout << "CATCH Copy Me" << std::endl; }
        CopyMe( CopyMe&& ) { std::cout << "CATCH Moved" << std::endl; }
        CopyMe( const char* s_ ):s{s_}{ std::cout << "CATCH Implicit conversion" << std::endl; }
};

void Run( CopyMe )
{
    std::cout << "CATCH Thread context" << std::endl; 
}


int main()
{
    std::cout << "CATCH Main Context" << std::endl;
    CopyMe me; 
    std::thread t( Run, "Hallo");
    t.join();
}

strace -xfo dump prog

grep 输出,我们得到:

6071  write(1, "CATCH Main Context\n", 19) = 19
6072  write(1, "CATCH Implicit conversion\n", 26) = 26
6072  write(1, "CATCH Thread context\n", 21) = 21

乌布斯!我们看到隐式转换的部分是在新创建的线程内部完成的!

【讨论】:

  • 传递给 std::thread 构造函数的 arg 是指向 char 的指针。该指针在新线程启动之前被复制,并且您的示例代码没有问题。如果您将指针传递给临时/局部变量,那么是的,您可以获得未定义的行为 - 但这并不特定于 std::thread,您会将其传递给任何试图保持指针并使用它的东西稍后。
  • @Mat:澄清一下:cppreference 上的文本意味着:所有参数都在调用之前复制,这意味着在我的示例中,我们得到了指向 const char 的指针的副本。但是隐式转换发生在新创建的线程中,从已经复制的指针开始……我现在就在吗?
  • 是的。您提供给std::thread 的构造函数的参数会被复制。对您的函数的调用(包括在必要时“创建”其参数)发生在新线程上。
猜你喜欢
  • 2020-01-07
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 2012-04-17
  • 2012-04-24
  • 1970-01-01
相关资源
最近更新 更多