提问人:Sami 提问时间:9/10/2023 最后编辑:Remy LebeauSami 更新时间:9/10/2023 访问量:95
std::lock 如何与 std::unique_lock 对象一起使用,而不是直接与 std::mutex 一起使用?
How does std::lock work with std::unique_lock objects instead of directly with std::mutex?
问:
我正在使用一段涉及银行账户转账的多线程代码。目标是在不遇到竞争条件的情况下安全地在账户之间转移资金。我用于在转账期间保护银行账户余额:std::mutex
我的问题围绕着 with 的使用 。我不是将对象直接传递给 ,而是将它们包装起来并将它们传递给 。std::unique_lock
std::lock
std::mutex
std::lock
std::unique_lock
std::lock
如何使用对象?std::lock
std::unique_lock
负责实际锁定和互斥锁,而对象仅管理锁(即,当它们超出范围时释放它们)?std::lock
from
to
std::unique_lock
调用方法吗?std::lock
lock()
std::unique_lock
与直接将对象传递给相比,使用有什么好处?std::unique_lock
std::lock
std::mutex
std::lock
struct bank_account
{
bank_account(int balance) :
mtx(), balance{ balance }
{}
std::mutex mtx;
int balance;
};
void transfer(bank_account& from, bank_account& to, int amount)
{
std::unique_lock<std::mutex> from_Lock(from.mtx, std::defer_lock);
std::unique_lock<std::mutex> to_Lock(to.mtx, std::defer_lock);
std::lock(from_Lock, to_Lock);
if (amount <= from.balance)
{
std::cout << "Before: " << amount << " from: " << from.balance << " to: " << to.balance << '\n';
from.balance -= amount;
to.balance += amount;
std::cout << "After: " << amount << " from: " << from.balance << " to: " << to.balance << '\n';
}
else
{
std::cout << amount << " is greater than " << from.balance << '\n';
}
}
int main()
{
bank_account A(200);
bank_account B(100);
std::vector<std::jthread> workers;
workers.reserve(20);
for (int i = 0; i < 10; ++i)
{
workers.emplace_back(transfer, std::ref(A), std::ref(B), 20);
workers.emplace_back(transfer, std::ref(B), std::ref(A), 10);
}
}
答:
std::lock
的目的是提供多个可锁定对象的无死锁锁定(参见 libc++ 实现)。
经典问题是,如果您有两个锁 L1 和 L2,并且
- 一个线程先锁定 L1,然后锁定 L2,然后
- 另一个线程锁定 L2,然后锁定 L1,
然后可能会出现死锁,因为每个线程可以保存一个锁,而另一个线程需要另一个锁。当您锁定并进入以下内容时,此问题适用:from.mtx
to.mtx
std::unique_lock<std::mutex> from_Lock(from.mtx, std::defer_lock); std::unique_lock<std::mutex> to_Lock(to.mtx, std::defer_lock); std::lock(from_Lock, to_Lock);
std::lock
执行 和 的无死锁锁定,并执行其余部分(即 RAII 内容)。from_Lock
to_Lock
std::unique_lock
Q&A问答
如何使用对象?
调用方法吗?std::lock
std::unique_lock
std::lock
lock()
std::unique_lock
std::unique_lock
是可锁定的,并将调用它,然后是互斥锁。std::lock
lock()
lock()
负责实际锁定和互斥锁,而对象仅管理锁(即,当它们超出范围时释放它们)?
std::lock
from
to
std::unique_lock
std::unique_lock
完全能够自行锁定和解锁互斥锁。它唯一不能做的就是在涉及多个锁时实现无死锁锁定。
与直接将对象传递给相比,使用有什么好处?
std::unique_lock
std::lock
std::mutex
std::lock
之后,您必须手动解锁两个互斥锁,这很容易出错。这是一个与 vs. / 类似的问题。如果您立即将两个互斥锁包装在一个 不过,那就好了。std::unique_ptr
new
delete
std::lock_guard
进一步改进
对于 ,您可以使用比以下更简单的锁:std::lock
std::unique_lock
std::lock(from.mtx, to.mtx);
std::lock_guard<std::mutex> from_lock(from.mtx, std::adopt_lock);
std::lock_guard<std::mutex> to_lock(to.mtx, std::adopt_lock);
您只需要在想转让所有权时;否则,您可以使用(这是一个稍微简单的类型)。std::unique_lock
std::lock_guard
如果您使用的是 C++17,则使用 std
::scoped_lock 会变得更加简单:
// CTAD, equivalent to std::scoped_lock<std::mutex, std::mutex> lock(...)
std::scoped_lock lock(from.mtx, to.mtx);
std::scoped_lock
是构造函数的替代品,并且具有内置于无死锁的锁定,类似于使用 。std::lock_guard
std::lock
Смотритетакже: 锁定多个 std::mutex 的最佳方法是什么?
评论
std::lock
lock()
try_lock()
评论
:unique_lock 和 std
::lock()
的文档?