gpt4 book ai didi

c++ - 线程安全的复制构造函数/赋值运算符

转载 作者:可可西里 更新时间:2023-11-01 18:35:03 24 4
gpt4 key购买 nike

假设我们想使用 std::mutex 使类 A 成为线程安全的。我的复制构造函数和赋值运算符类似于以下代码:

#include <mutex>

class A {
private:
int i;
mutable std::mutex mtx;

public:
A() : i(), mtx() { }

A(const A& other) : i(), mtx()
{
std::lock_guard<std::mutex> _lock(other.mtx);
i = other.i;
}

A& operator=(const A& other)
{
if (this!=&other) {
std::lock_guard<std::mutex> _mylock(mtx), _otherlock(other.mtx);
i = other.i;
}
return *this;
}

int get() const
{
std::lock_guard<std::mutex> _mylock(mtx);
return i;
}
};

我不认为它有任何问题,除了 other 在被复制之前被另一个线程破坏的可能性,我可以处理。

我的问题是我在任何地方都没有看到这种模式,所以我不知道人们是否只是不需要它,或者由于我目前没有看到的原因,它显然是错误的。

谢谢

注意事项:

这只是一个例子。我可以有任意数量的任何类型的成员变量,它不必只是一个 int

在 Martin York 对可能的死锁发表评论之后,这是一个使用 copy-and-swap 的更新版本(复制省略也是可能的,但它不能有效地处理 self 分配的情况)。

我也把int改成T了,所以大家不能认为是POD。

template<typename T>
class A {
private:
T t;
mutable std::mutex mtx;

public:
A() : t(), mtx() { }

A(const A& other) : t(), mtx()
{
std::lock_guard<std::mutex> _lock(other.mtx);
t = other.t;
}

A& operator=(const A& other)
{
if (this!=&other) {
A tmp(other);
std::lock_guard<std::mutex> _lock(mtx);
std::swap(t, tmp.t);
}
return *this;
}

T get() const
{
std::lock_guard<std::mutex> _lock(mtx);
return t;
}

};

最佳答案

老问题,新答案:

恕我直言,处理原始复制赋值运算符死锁问题的更好方法是:

  A& operator=(const A& other)
{
if (this!=&other) {
std::unique_lock<std::mutex> _mylock(mtx, std::defer_lock),
_otherlock(other.mtx, std::defer_lock);
std::lock(_mylock, _otherlock);
i = other.i;
}
return *this;
}

即使用 std::lock(L1, L2)同时锁定两个互斥量而不用担心死锁。这可能比复制/交换习惯用法具有更高的性能,尤其是当成员数据由 std::vector 组成时。 , std::string ,或包含 vector 和/或字符串的类型。

在C++1y中(我们希望y是4),有一个新的<shared_mutex> header 提供读/写锁定功能,可能可以提高性能(特定用例需要进行性能测试以确认这一点)。以下是它的使用方式:

#include <mutex>
#include <shared_mutex>

class A {
private:
int i;
mutable std::shared_mutex mtx;

public:
A() : i(), mtx() { }

A(const A& other) : i(), mtx()
{
std::shared_lock<std::shared_mutex> _lock(other.mtx);
i = other.i;
}

A& operator=(const A& other)
{
if (this!=&other) {
std::unique_lock<std::shared_mutex> _mylock(mtx, std::defer_lock);
std::shared_lock<std::shared_mutex> _otherlock(other.mtx, std::defer_lock);
std::lock(_mylock, _otherlock);
i = other.i;
}
return *this;
}

int get() const
{
std::shared_lock<std::shared_mutex> _mylock(mtx);
return i;
}
};

即这与原始代码非常相似(修改为使用 std::lock,正如我在上面所做的那样)。但是成员互斥量类型现在是 std::shared_mutex而不是 std::mutex .在保护 const A 时(并假设除了互斥体之外没有可变成员),只需要将互斥体锁定在“共享模式”。使用 shared_lock<shared_mutex> 很容易做到这一点.当你需要以“独占模式”锁定互斥锁时,你可以使用unique_lock<shared_mutex> , 或 lock_guard<shared_mutex>视情况而定,并以与 std::mutex 一起使用此工具的相同方式.

特别注意,现在许多线程可以同时从同一个 A 复制构造,甚至复制 from 相同的 A .但是只有一个线程仍然可以复制赋值相同的A一次。

关于c++ - 线程安全的复制构造函数/赋值运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4037853/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com