具有互斥量的多个锁和死锁的可能性
Multiple locks with mutex and the possibility of a deadlock
我是线程的新手,正在尝试了解互斥量。
我将互斥量理解为仅由一个线程选取的某个对象(键)(如果它被选取,则其他线程不能选择它并且必须等待)才能访问我们想要锁定的代码的某些部分。
因此当时只有一个线程可以访问代码的锁定部分(例如共享计数器)。其他线程将不得不等到互斥体解锁等等。
Mutex1.Lock();
{
Mutex2.Lock();
{
// Code locked by mutex 1 and 2.
}
Mutex2.Unlock();
// Code locked by mutex 1.
}
Mutex1.Unlock();
如果我写多个互斥锁会怎样?
两个互斥锁会被同一个线程选中吗?我还读到多个互斥锁可能会导致死锁。
谁能解释并提供一个示例,说明如何通过使用 2 个互斥锁锁定部分代码来导致死锁?
一个线程可以持有多个锁,是的。而且即使只获取了一个mutex,也确实会出现死锁。看工作流程:
线程 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!
僵局!
阅读 this,一些互斥锁类型,如 PTHREAD_MUTEX_NORMAL
也可能导致线程自身死锁。
.
.
.
lock mutex
.
.
.
try lock
What happens if I write multiple mutex locks? Will both mutexes picked
by the same thread?
一个线程可以同时持有任意数量的互斥体。在您的示例代码中,进入 Mutex1
保护区域的线程将尝试获取 Mutex2
,等待另一个线程在必要时首先释放它。您提供的代码中没有特别的理由认为它不会成功。
I also read that multiple mutex locks may cause
deadlock. Could anyone explain me how could I cause deadlock by
locking part of code with 2 mutexes?
假设有两个线程,一个持有 mutex1,另一个持有 mutex2(在这种情况下,至少后者必须 运行 与您的伪代码不同)。现在假设每个线程都试图获取另一个互斥量而不释放它已经持有的互斥量。第一个线程必须获取 mutex2 才能继续,并且在另一个线程释放它之前不能这样做。第二个线程必须获取 mutex1 才能继续,并且在另一个线程释放它之前不能这样做。因此,两个 线程都永远 不能继续——这是一个死锁。
一般规则是这样的:如果有一组互斥量,所有这些互斥量可能每个线程都想同时持有,那么必须有一个固定的相对顺序,每个线程在获取时都遵循这些互斥量它们的任何子集。
这是对您描述的内容进行编码的安全方法:
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
的一个优点是您不必记住锁定互斥量所需的顺序(这使得大型代码库中的维护更容易)。但缺点是您需要在代码中的单个点上同时锁定两个锁。如果你不能同时锁定它们,那么你必须回到其他答案描述的顺序。
此代码也是异常安全的,因为如果在任何地方抛出任何异常任何地方,当异常传播出去时,任何被锁定的东西都会被解锁。
我是线程的新手,正在尝试了解互斥量。
我将互斥量理解为仅由一个线程选取的某个对象(键)(如果它被选取,则其他线程不能选择它并且必须等待)才能访问我们想要锁定的代码的某些部分。
因此当时只有一个线程可以访问代码的锁定部分(例如共享计数器)。其他线程将不得不等到互斥体解锁等等。
Mutex1.Lock();
{
Mutex2.Lock();
{
// Code locked by mutex 1 and 2.
}
Mutex2.Unlock();
// Code locked by mutex 1.
}
Mutex1.Unlock();
如果我写多个互斥锁会怎样?
两个互斥锁会被同一个线程选中吗?我还读到多个互斥锁可能会导致死锁。
谁能解释并提供一个示例,说明如何通过使用 2 个互斥锁锁定部分代码来导致死锁?
一个线程可以持有多个锁,是的。而且即使只获取了一个mutex,也确实会出现死锁。看工作流程:
线程 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!
僵局!
阅读 this,一些互斥锁类型,如 PTHREAD_MUTEX_NORMAL
也可能导致线程自身死锁。
.
.
.
lock mutex
.
.
.
try lock
What happens if I write multiple mutex locks? Will both mutexes picked by the same thread?
一个线程可以同时持有任意数量的互斥体。在您的示例代码中,进入 Mutex1
保护区域的线程将尝试获取 Mutex2
,等待另一个线程在必要时首先释放它。您提供的代码中没有特别的理由认为它不会成功。
I also read that multiple mutex locks may cause deadlock. Could anyone explain me how could I cause deadlock by locking part of code with 2 mutexes?
假设有两个线程,一个持有 mutex1,另一个持有 mutex2(在这种情况下,至少后者必须 运行 与您的伪代码不同)。现在假设每个线程都试图获取另一个互斥量而不释放它已经持有的互斥量。第一个线程必须获取 mutex2 才能继续,并且在另一个线程释放它之前不能这样做。第二个线程必须获取 mutex1 才能继续,并且在另一个线程释放它之前不能这样做。因此,两个 线程都永远 不能继续——这是一个死锁。
一般规则是这样的:如果有一组互斥量,所有这些互斥量可能每个线程都想同时持有,那么必须有一个固定的相对顺序,每个线程在获取时都遵循这些互斥量它们的任何子集。
这是对您描述的内容进行编码的安全方法:
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
的一个优点是您不必记住锁定互斥量所需的顺序(这使得大型代码库中的维护更容易)。但缺点是您需要在代码中的单个点上同时锁定两个锁。如果你不能同时锁定它们,那么你必须回到其他答案描述的顺序。
此代码也是异常安全的,因为如果在任何地方抛出任何异常任何地方,当异常传播出去时,任何被锁定的东西都会被解锁。