安全移动 运行 访问其他成员的成员线程中的 lambda 对象

Safely moving objects that run a lambda in a member thread that accesses other members

我有一个 class,它有一个 std::thread 成员变量,它运行一个依赖于其他成员变量的 lambda 函数。

举个例子:

struct C {
    C() {
        thread_ = std::thread([this]() {
            running_ = true;
            while (running_) {
                ...
            }
        });
    }
    C(C&& rv) {
        swap(rv);
    }
    void swap(C& rhs) {
        std::swap(thread_, rhs.thread_); // step 1
        std::swap(running_, rhs.running_); // step 2
    }
    std::thread thread_;
    bool running_;
};

int main() {
    C c;
    C c2 = move(c); // Is c safely moved to c2?
}

虽然 std::thread 当然是可移动的,但这样做并不会神奇地修改任何引用它的指针,或者它包含的对象,如果有的话。

因此,即使在您移动 C 之后,包含的 std::thread 管理的线程仍然会引用它之前引用的对象。

所以,如果你想安全地move/swap一个C,使用pimpl-idiom:
仅保护指向线程可能访问的资源的指针,并将该指针指向它。
在您的情况下,这意味着:

  1. 移动running_:

    struct inner {
        std::atomic<bool> running;
    };
    unique_ptr<inner> data_ = new inner;
    
  2. 传递指向的数据:

    auto data = &*data_;
    thread_ = std::thread([data]() {
        data->running = true;
        while (data->running) {
            ...
        }
    });
    

(也可选择移动未被线程访问的数据。)

此外,即使您没有询问,您的 running 也必须是 std::atomic<bool>,这样检查才有效。