在C中添加互斥锁后的struct中的赋值运算符

在C中添加互斥锁后的struct中的赋值运算符,第1张

概述我有一个结构类型: struct MyStruct { int field1; int field2;} 然后有必要添加一个互斥量,使其在线程之间共享: struct MyStruct { std::mutex _mutex; int field1; int field2;} 但是编译器(clang)在行上给我这些消息,我将一个现有结构分配给MyStr 我有一个结构类型:

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’的复制赋值运算符被隐式删除,因为字段’_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;       ^

请帮助:如何重写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;}

你不应该这样做的原因是假设线程A这样做:

x = y;

同时线程B执行此 *** 作:

y = x;

现在你有可能陷入僵局.想象一下线程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;}

std :: lock(m1,m2,…)的工作是以某种不会死锁的神奇方式锁定所有互斥锁.有关您可能想知道如何完成的更多细节,您可以阅读Dining Philosophers Rebooted.

现在锁定了_mutex和o._mutex,只需要让lock_guards采用其互斥锁的所有权,就可以使解锁异常安全.也就是说,他们将不再试图将他们的互斥锁锁定在构造上,但是他们仍然会在破坏时解锁它们. lock_guard构造函数本身不会抛出任何内容,因此这都是异常安全的.

哦,你还必须将_mutex存储为可变数据成员,否则你将无法在rhs上锁定和解锁它.

在C 14中,如果您想尝试,可以使用潜在的优化:您可以“写锁定”此 – > _mutex和“read-lock”o._mutex.如果没有线程分配给该rhs,这将允许多个线程同时从公共rhs分配.为了做到这一点,你需要让MyStruct存储一个std :: shared_timed_mutex而不是std :: 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;    }};

这类似于之前的情况,除了我们需要更改互斥锁的类型,现在lhs锁定使用unique_lock(写入锁定lhs互斥锁)和rhs锁定使用shared_lock(读取锁定rhs互斥锁).这里我们还使用std :: defer_lock来构造锁,但是告诉锁定互斥锁还没有锁定,并且不要锁定构造.然后我们的老朋友std :: lock(m1,m2)用于告诉两个锁同时锁定没有死锁.是的,std :: lock适用于互斥锁和锁类型.任何具有成员lock(),try_lock()和unlock()的东西都可以使用std :: lock(m1,…).

注意,C 14技术绝对不是一种优化.您必须进行测量以确认或否认它是.对于像MyStruct这样简单的东西,它可能不是一个优化,除了一些特殊的使用模式.使用std :: mutex的C 11技术仍然是工具箱中的一个有价值的工具,即使在C 14中也是如此.

为了便于在互斥锁和shared_timed_mutex之间切换回第四和第四,这个最新的例子使用了类型别名,可以很容易地改变它.只需更改两行即可切换回互斥锁:

使用MutexType = std :: shared_timed_mutex;

使用ReadLock = std :: sharedunique_lock< MutexType&gt ;; 使用WriteLock = std :: unique_lock< MutexType&gt ;;

总结

以上是内存溢出为你收集整理的在C中添加互斥锁后的struct中的赋值运算符全部内容,希望文章能够帮你解决在C中添加互斥锁后的struct中的赋值运算符所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1218238.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-05
下一篇 2022-06-05

发表评论

登录后才能评论

评论列表(0条)

保存