在线程之间移动向量

时间:2014-01-22 10:54:59

标签: c++ multithreading c++11 move-semantics

我有一个不断收集数据项的线程,如下所示:

class MyThread {
public:
    class Item {
        // ...
    };

    startup();
    shutdown();

    bool hasItems() const;

    // retrieve collected items
    std::vector<Item>&& items();
private:
    std::mutex itemMutex;
    std::vector<Item> currentItems;
};

检索项目还应清除线程的项目列表。我返回一个右值,以便在调用端调用移动构造函数。当然,检索项应该是线程安全的,因此实现如下所示:

std::vector<MyThread::Item>&& MyThread::items() {
    std::lock_guard<std::mutex> lock(itemMutex);
    return std::move(currentItems);
}

我认为锁定在这里发布得太早:函数返回rvalue'd向量,但是当std::lock_guard被销毁并释放互斥锁时,移动构造函数不一定会被调用。所以从我的理解来看,这不是线程安全的。我对吗?如何使其成为线程安全的?

2 个答案:

答案 0 :(得分:9)

你是对的,它不是线程安全的,因为在方法返回后会发生移动,此时锁定将被释放。要修复它,只需将向量移动到局部变量并返回:

std::vector<MyThread::Item> MyThread::items() 
{
    std::lock_guard<std::mutex> lock(itemMutex);
    return std::vector<MyThread::Item>(std::move(currentItems));
}

答案 1 :(得分:0)

你必须记住,currentItem在移动后处于“部分形成”状态(见What lasts after using std::move c++11)。 在其中一个答案中,有一个例子可以解决这个问题:

std::vector<MyThread::Item> MyThread::items() 
{
    std::lock_guard<std::mutex> lock(itemMutex);
    auto tmp =  std::move(currentItems);
    currentItems.clear();
    return tmp;
} 

我宁愿选择最直接的解决方案,让编译器完成繁重的工作:

std::vector<MyThread::Item> MyThread::items() 
{
    std::lock_guard<std::mutex> lock(itemMutex);
    std::vector<MyThread::Item> tmp;
    tmp.swap(currentItems);
    return tmp;
}