Sam*_*ami 3 c++ multithreading deadlock unique-lock
我正在使用一段涉及银行帐户转账的多线程代码。目标是在账户之间安全地转账,而不会遇到竞争条件。我用来std::mutex在转账过程中保护银行账户余额:
std::unique_lock我的问题集中在with的使用上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对象传递给相比,使用 with 有何优点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);
}
}
Run Code Online (Sandbox Code Playgroud)
目的std::lock是提供多个Lockable对象的无死锁锁定(参见libc++实现) 。经典问题是,如果您有两个锁L1和L2,并且
那么可能会出现死锁,因为每个线程都可以持有一个锁,并需要另一个线程的另一个锁。当您锁定from.mtx并to.mtx进入以下情况时,会出现此问题:
Run Code Online (Sandbox Code Playgroud)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::lockfrom_Lock执行和的无死锁锁定to_Lock,并std::unique_lock执行其余操作(即 RAII 内容)。
如何
std::lock与对象一起工作std::unique_lock?
是否std::lock调用lock()的方法std::unique_lock?
std::unique_lock是Lockable,并且std::lock会调用lock()它,然后它lock()是互斥体。
负责
std::lock实际锁定from和to互斥锁,而std::unique_lock对象仅管理锁(即,当它们超出范围时释放它们)?
std::unique_lock完全有能力自行锁定和解锁互斥锁。它唯一不能做的是在涉及多个锁时实现无死锁锁定。
std::unique_lock与std::lock直接将std::mutex对象传递给相比,使用 with 有何优点std::lock?
之后您必须手动解锁两个互斥体,这很容易出现错误。std::unique_ptr这是与vs. new/类似的问题delete。如果您立即将两个互斥体包装在一个 while 中,那就没问题了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);
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
std::scoped_lock是构造函数中内置的无死锁锁定的替代品std::lock_guard,类似于使用std::lock.