在 c++++ 多线程编程中,死锁的成因主要有:1. 互斥锁不当使用;2. 顺序锁定。在实战中,如果多个线程同时尝试获取同一组锁,按照不同的顺序获取,就可能导致死锁。可以通过始终按照相同的顺序获取锁来避免这种情况。
在 C++ 多线程编程中导致死锁的成因
死锁是一个并发编程中常见的错误,它发生在一个或多个线程等待另一个线程释放锁,而另一个线程又在等待前者释放锁。这会导致程序陷入僵持,无法继续执行。
在 C++ 中,死锁通常由以下原因引起:
- 互斥锁不当使用:如果没有正确地使用互斥锁,线程可能会尝试同时获取同一锁,导致死锁。
- 顺序锁定:如果线程需要获取多个锁,它们应该总是按照相同的顺序获取这些锁。否则,可能导致死锁,因为一个线程可能会等待另一线程释放一个锁,而另一线程又等待该线程释放另一个锁。
实战案例:
考虑以下代码:
class BankAccount {
public:
std::mutex m_mutex; // 互斥锁
int balance = 0;
};
void transfer(BankAccount &from, BankAccount &to, int amount) {
std::lock_guard<std::mutex> lock1(from.m_mutex); // 锁定第一个账户
std::lock_guard<std::mutex> lock2(to.m_mutex); // 锁定第二个账户
// 从第一个账户扣除金额
from.balance -= amount;
// 将金额添加到第二个账户
to.balance += amount;
}
在这个例子中,如果两个线程同时调用 transfer() 函数,且它们试图将钱从不同的账户转移到同一个账户,就会发生死锁。这是因为一个线程会先锁定第一个账户,然后等待另一个线程释放第二个账户,而另一个线程又会先锁定第二个账户,然后等待第一个线程释放第一个账户。
为了避免这种情况,线程应该始终按照相同的顺序获取锁,例如:
void transfer(BankAccount &from, BankAccount &to, int amount) {
// 按照账户 ID 排序账户
if (from.getId() < to.getId()) {
std::lock_guard<std::mutex> lock1(from.m_mutex);
std::lock_guard<std::mutex> lock2(to.m_mutex);
} else {
std::lock_guard<std::mutex> lock2(to.m_mutex);
std::lock_guard<std::mutex> lock1(from.m_mutex);
}
// 从第一个账户扣除金额
from.balance -= amount;
// 将金额添加到第二个账户
to.balance += amount;
}
通过按照账户 ID 排序账户并按照相同的顺序锁定它们,我们可以防止这种情况发生。