将函数对象传递给线程对象的构造函数-复制吗?

时间:2019-04-01 00:25:02

标签: c++

在“ C ++并发行动”一书中有一个示例:


    class background_task
    {
    public:
        void operator() () const {
            do_something();
            do_something_else();
        }
    };

    background_task f;
    std::thread my_thread(f);

后跟文本:“在这种情况下,将提供的功能对象复制到属于新创建的执行线程的存储中,然后从那里调用。因此,至关重要的是,复制的行为应与原始行为或结果相同可能不是预期的。”

有人可以向我详细解释这两个句子的意思吗?可以提供给线程对象的构造函数的其他可调用类型呢,它们不会被复制吗?如何确保“副本的行为与原始行为相同”或为什么它的行为与行为不相同? 谢谢!

3 个答案:

答案 0 :(得分:3)

该语句将给定的示例与将普通函数void do_some_work();作为参数传递给std::thread之前的情况进行了对比。

std::thread my_thread(do_some_work);

功能对象background_task f应该是可复制的。如果class background_task由使其不可复制的内容组成,则这是不可能的。例如,如果 background_task有一个std::mutex,由于std::mutex是不可复制或不可移动的,因此无法复制。请参阅 Demo

第二件事是,即使background_task是可复制的,也应确保副本构造函数生成原始副本的精确副本。一个没有发生这种情况的例子是众所周知的情况,其中该类具有原始指针,并且复制构造函数不进行深度复制。

可调用void do_some_work();也像函数对象一样被复制到线程中,但是不会遇到上述问题。

答案 1 :(得分:3)

std::thread的所有权语义与std::unique_ptr的所有权语义相同。尽管std::thread实例不像std::unique_ptr那样拥有动态对象,但它拥有资源。换句话说,每个实例负责管理执行线程。此所有权可以在实例之间转移,因为std::thread的实例是可移动的,即使它们不是可复制的。因此,在任何时候,只有一个对象与特定的执行线程相关联,同时为程序员提供了在对象之间转移所有权的选项。

在某些情况下,我们需要将所有权移至另一个线程。假设我们要编写一个函数来创建要在后台中运行的线程。但是我们希望新线程的所有权传递回调用函数,而不是等待它完成。在另一种情况下,我们创建一个线程并将所有权传递给某个函数,该函数应等待其完成。对于这两种情况,我们都需要将所有权从一个地方转移到另一个地方。

这就是std::thread需要支持此举的原因。 C ++标准库中的许多资源拥有类型,例如std::unique_ptr,可以移动,但不能可复制,而std::thread是其中之一。这意味着可以在std::thread个实例之间移动特定执行线程的所有权。

最后,正如@ P.W所说:“函数对象background_task f应该是可复制的。如果class background_task类包含使其无法复制的内容,则这是不可能的。”

答案 2 :(得分:-1)

这里没有什么要读的东西。

无论可调用对象是什么,都必须将其复制到线程中,否则调用它将不是线程安全的。

如果可调用对象的类型具有必须说的“意外的”复制语义(认为是一个很奇怪的书面复制构造函数),那么对于任何试图使用您的代码的人来说,这都是个坏消息。

就是这样!