我是线程新手并试图理解互斥锁。
我理解互斥锁是一个只被一个线程挑选的对象(密钥)(如果它被选中则其他线程无法选择它必须等待)访问我们想要锁定的代码的一部分。
因此,当时只有一个线程可以访问该锁定的代码部分(例如共享计数器)。其他线程必须等到互斥锁解锁后再等等。
Mutex1.Lock();
{
Mutex2.Lock();
{
// Code locked by mutex 1 and 2.
}
Mutex2.Unlock();
// Code locked by mutex 1.
}
Mutex1.Unlock();
如果我写多个互斥锁会怎么样?
两个互斥锁是否会被同一个线程选中?我还读到多个互斥锁可能导致死锁。
任何人都可以解释并提供一个示例,说明如何通过使用2个互斥锁锁定部分代码来导致死锁?
答案 0 :(得分:4)
线程可以容纳多个锁,是的。即使它只获得了一个互斥锁,也可能发生死锁。查看工作流程:
主题A
.
.
.
lock mutex1
.
<---- Context switch! ----->
主题B
.
.
.
.
lock mutex2
.
.
.
try lock mutex1 ----> BLOCKED UNTIL THREAD A RELEASES LOCK!
主题A
.
.
.
try lock mutex2 ---> BLOCKED UNTIL THREAD B RELEASES LOCK!
<强> DEADLOCK!强>
阅读this,某些互斥体类型,如PTHREAD_MUTEX_NORMAL
可能导致线程自身死锁。
.
.
.
lock mutex
.
.
.
try lock
答案 1 :(得分:4)
以下是对您描述的内容进行编码的安全方式:
std::mutex Mutex1;
std::mutex Mutex2;
void
test()
{
using namespace std;
lock(Mutex1, Mutex2); // no deadlock!
lock_guard<mutex> lk1{Mutex1, adopt_lock};
{
lock_guard<mutex> lk2{Mutex2, adopt_lock};
// Code locked by mutex 1 and 2.
} // lk2.unlock()
// Code locked by mutex 1.
} // lk1.unlock()
此代码无法死锁,因为std::lock(Mutex1, Mutex2)
会锁定两个互斥锁,同时避免死锁(通过某些内部算法)。使用std::lock
的一个优点是您不必记住锁定互斥锁所需的顺序(这使得在大型代码库中维护更容易)。但缺点是您需要在代码中的单个点锁定两个锁。如果你不能同时锁定它们,那么你必须按照其他答案描述的那样回归订购。
此代码也是异常安全的,因为如果在任何地方任何地方抛出任何异常,则在异常传播时解锁任何被锁定的内容。
答案 2 :(得分:2)
如果我写多个互斥锁会怎么样?两个互斥体都会被选中 由同一个线程?
线程可以同时保存任意数量的互斥锁。在您的示例代码中,进入受Mutex1
保护的区域的线程将尝试获取Mutex2
,等待另一个线程在必要时首先释放它。您提交的代码中没有特别的理由认为它不会成功。
我还读到多个互斥锁可能会导致 僵局。任何人都可以解释我怎么能造成死锁 使用2个互斥锁锁定部分代码?
假设有两个线程,一个持有mutex1而另一个持有mutex2(在这种情况下,至少后者必须运行与你的伪代码不同的东西)。现在假设每个线程都试图获取另一个互斥锁而不释放它已经拥有的互斥锁。第一个线程必须获取mutex2才能继续,并且在另一个线程释放之前不能这样做。第二个线程必须获取mutex1才能继续,并且在另一个线程释放之前不能这样做。 Ergo, 线程
一般规则是这样的:如果有一组互斥体,所有两个或多个线程可能都想要同时保持,那么每个线程在获取任何子集时都必须遵循这些互斥体的固定相对排序他们。