C++ 在C+;中添加互斥后在结构中的赋值运算符+;
我有一个结构类型:C++ 在C+;中添加互斥后在结构中的赋值运算符+;,c++,c++11,struct,mutex,assignment-operator,C++,C++11,Struct,Mutex,Assignment Operator,我有一个结构类型: struct MyStruct { int field1; int field2; } 然后,有必要向其添加互斥体,以使其在线程之间共享: struct MyStruct { std::mutex _mutex; int field1; int field2; } 但是编译器(clang)在我将一个现有结构赋值给MyStruct类型的变量的行上给我这些消息,比如MyStruct MyStruct=p_MyStructMap->at
struct MyStruct {
int field1;
int field2;
}
然后,有必要向其添加互斥体,以使其在线程之间共享:
struct MyStruct {
std::mutex _mutex;
int field1;
int field2;
}
但是编译器(clang)在我将一个现有结构赋值给MyStruct类型的变量的行上给我这些消息,比如MyStruct MyStruct=p_MyStructMap->at(clientId)代码>:
(1) 错误:“MyStruct”类型的对象无法分配,因为其复制分配运算符已被隐式删除
(2) 注意:“MyStruct”的复制分配运算符被隐式删除,因为字段“\u mutex”有一个已删除的复制分配运算符
std::mutex _mutex
^
(3) /usr/bin/./lib/gcc/x86_64-linux-gnu/4.8/../../../../../../../../include/c++/4.8/mutex:129:12:注意:函数已在此处明确标记为删除
mutex& operator=(const mutex&) = delete;
^
请提供帮助:如何重写结构或程序逻辑以使用此结构的互斥体?std::无法分配互斥体变量/成员,因此,您会遇到错误
试着这样做:
struct MyStruct {
std::mutex _mutex;
int field1;
int field2;
MyStruct &operator=(const MyStruct &o) {
field1=o.field1;
field2=o.field2;
return *this;
}
};
MyStruct& operator=(const MyStruct& o)
{
if (this != &o)
{
std::lock(_mutex, o._mutex);
std::lock_guard<std::mutex> lhs_lk(_mutex, std::adopt_lock);
std::lock_guard<std::mutex> rhs_lk(o._mutex, std::adopt_lock);
field1 = o.field1;
field2 = o.field2;
}
return *this;
}
但是,这不会在分配期间锁定互斥锁。根据上下文的不同,您可能需要在赋值运算符或调用函数中添加锁定。假设添加了\u互斥体
,以保护其他字段不被同时访问,您将需要在赋值期间锁定互斥体,除非您可以保证多个线程不会访问赋值表达式的任一侧,否则:
x=y
如果任何其他线程同时读取或写入x
,则有一个未锁定的争用
如果任何其他线程同时写入y
,则您有一个未锁定的争用
如果您确实需要锁定,它并不像锁定两侧那么简单:
MyStruct& operator=(const MyStruct& o)
{
if (this != &o)
{
// WRONG! DO NOT DO THIS!!!
std::lock_guard<std::mutex> lhs_lk(_mutex);
std::lock_guard<std::mutex> rhs_lk(o._mutex);
field1 = o.field1;
field2 = o.field2;
}
return *this;
}
线程B同时执行以下操作:
x = y;
y = x;
现在有可能出现死锁。假设线程A锁定x.\U互斥锁,然后线程B锁定y.\U互斥锁。现在,线程A和线程B都将阻止尝试锁定它们的o.\u互斥锁
,并且两个线程都不会成功,因为它正在等待另一个线程释放它
赋值运算符的正确公式如下所示:
struct MyStruct {
std::mutex _mutex;
int field1;
int field2;
MyStruct &operator=(const MyStruct &o) {
field1=o.field1;
field2=o.field2;
return *this;
}
};
MyStruct& operator=(const MyStruct& o)
{
if (this != &o)
{
std::lock(_mutex, o._mutex);
std::lock_guard<std::mutex> lhs_lk(_mutex, std::adopt_lock);
std::lock_guard<std::mutex> rhs_lk(o._mutex, std::adopt_lock);
field1 = o.field1;
field2 = o.field2;
}
return *this;
}
这与以前类似,只是我们需要更改互斥锁的类型,现在lhs使用唯一锁
(写入锁定lhs互斥锁)锁定,rhs使用共享锁
(读取锁定rhs互斥锁)。这里我们还使用std::defer_lock
来构造锁,但告诉锁互斥锁尚未锁定,并且不锁定构造。然后我们的老朋友std::lock(m1,m2)
被用来告诉两个锁同时锁定,而不会出现死锁。是的,std::lock
同时适用于互斥和锁类型。任何具有成员lock()
、try_lock()
和unlock()
的内容都将使用std::lock(m1,m2,…)
注意,C++14技术并不一定是一种优化。你必须测量以确认或否认这一点。对于像MyStruct
这样简单的东西,除了一些特殊的使用模式之外,它可能不是一种优化。带有std::mutex的C++11技术仍然是工具箱中的一个有价值的工具,即使在C++14中也是如此
为了便于在互斥体
和共享_timed_mutex
之间切换回第四个互斥体和第四个互斥体,此最新示例使用了类型别名,可以轻松更改。只需更改两行即可切换回互斥体:
#include <mutex>
#include <shared_mutex>
struct MyStruct
{
using MutexType = std::shared_timed_mutex;
using ReadLock = std::shared_lock<MutexType>;
using WriteLock = std::unique_lock<MutexType>;
mutable MutexType _mutex;
int field1;
int field2;
MyStruct& operator=(const MyStruct& o)
{
if (this != &o)
{
WriteLock lhs_lk(_mutex, std::defer_lock);
ReadLock rhs_lk(o._mutex, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
field1 = o.field1;
field2 = o.field2;
}
return *this;
}
};
使用MutexType=std::
shared\u timed\ucode>mutex代码>
使用ReadLock=std::
共享唯一锁代码>
使用WriteLock=std::unique_lock
您可以定义自己的赋值运算符,它不会影响互斥(除了锁定之外)。很可能您想要MyStruct&MyStruct=p\u MyStructMap->at(clientId)代码>感谢您的详细回答,但是,我遇到了std::lock(_mutex,o._mutex)的问题代码>,我得到错误“传递…'this'
参数丢弃限定符”。我已经把它缩小到o._mutex
作为罪魁祸首,这很有道理b/co
是const
,当然std::lock
想要修改o
的mutex。给出的答案是否有缺陷,或者我遗漏了一些简单的东西?不幸的是,我目前只使用c++11。当然,如果有必要的话,我可以问一个新问题。让您的数据成员互斥可变。