vla*_*don 3 c++ struct mutex assignment-operator c++11
我有一个结构类型:
struct MyStruct {
int field1;
int field2;
}
Run Code Online (Sandbox Code Playgroud)
然后有必要添加一个互斥量,使其在线程之间共享:
struct MyStruct {
std::mutex _mutex;
int field1;
int field2;
}
Run Code Online (Sandbox Code Playgroud)
但是然后编译器(clang)在行上给我这些消息,我将一个现有结构分配给MyStruct类型的变量,如MyStruct mystruct = p_MyStructMap->at(clientId);:
(1)错误:无法分配"MyStruct"类型的对象,因为隐式删除了其复制赋值运算符
(2)注意:'MyStruct'的复制赋值运算符被隐式删除,因为字段'_mutex'有一个删除的复制赋值运算符
std::mutex _mutex
^
Run Code Online (Sandbox Code Playgroud)
(3)/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/mutex:129:12:注意:功能已在此明确标记删除
mutex& operator=(const mutex&) = delete;
^
Run Code Online (Sandbox Code Playgroud)
请帮助:如何重写struct或者可能是为此结构使用互斥锁的程序逻辑?
假设_mutex已添加以保护其他字段不被同时访问,您将需要在分配期间锁定互斥锁,除非您可以保证多个线程不访问赋值表达式的任何一侧:
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;
}
Run Code Online (Sandbox Code Playgroud)
你不应该这样做的原因是假设线程A这样做:
x = y;
Run Code Online (Sandbox Code Playgroud)
同时线程B执行此操作:
y = x;
Run Code Online (Sandbox Code Playgroud)
现在你有可能陷入僵局.想象一下线程A锁定x._mutex,然后线程B锁定y._mutex.现在,线程A和B都将阻止尝试锁定它们o._mutex,并且两个线程都不会成功,因为它正在等待另一个线程释放它.
赋值运算符的正确公式如下所示:
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;
}
Run Code Online (Sandbox Code Playgroud)
我们的工作std::lock(m1, m2, ...)是以一种不会死锁的神奇方式锁定所有互斥锁.有关您可能想知道如何完成的更多细节,您可以阅读Dining Philosophers Rebooted.
现在用_mutex和o._mutex锁定,您只需让自己的解锁异常安全的通过使lock_guard小号采用了互斥的所有权.也就是说,他们将不再试图将他们的互斥锁锁定在构造上,但是他们仍然会在破坏时解锁它们.该lock_guard构造投身什么,所以这是所有的异常安全的.
哦,你还必须存储_mutex为mutable数据成员,否则你将无法在rhs上锁定和解锁它.
在C++ 14中,如果您想尝试它,可以使用潜在的优化:您可以"写锁定" this->_mutex和"读锁定" o._mutex.如果没有线程分配给该rhs,这将允许多个线程同时从公共rhs分配.为了做到这一点,你需要存储一个而不是:MyStructstd::shared_timed_mutexstd::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;
}
};
Run Code Online (Sandbox Code Playgroud)
这与之前类似,只是我们需要更改互斥锁的类型,现在lhs锁定unique_lock(写入锁定lhs互斥锁)和rhs锁定shared_lock(读取锁定rhs互斥锁).这里我们也std::defer_lock用来构造锁,但告诉锁,互斥锁还没有锁定,并且不要锁定构造.然后我们的老朋友std::lock(m1, m2)用来告诉两个锁同时锁定没有死锁.是的,std::lock适用于互斥锁和锁类型.任何拥有成员lock(),try_lock()和unlock()将使用的东西std::lock(m1, m2, ...).
注意,C++ 14技术绝对不是一种优化.您必须进行测量以确认或否认它是.对于简单的事情,MyStruct它可能不是优化,除了一些特殊的使用模式.std::mutex即使在C++ 14中,C++ 11技术仍然是工具箱中的一个有价值的工具.
为了便于在切换回和第四之间mutex和shared_timed_mutex,这最新例子使用类型别名,它可以容易地改变的.只需更改两行即可切换回mutex:
using MutexType = std::shared_timed_mutex;
using ReadLock = std::共享unique_lock<MutexType>;
using WriteLock = std::unique_lock<MutexType>;
| 归档时间: |
|
| 查看次数: |
3077 次 |
| 最近记录: |