我有一个不断收集数据项的线程,如下所示:
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
被销毁并释放互斥锁时,移动构造函数不一定会被调用。所以从我的理解来看,这不是线程安全的。我对吗?如何使其成为线程安全的?
答案 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;
}