C++ Thread 死锁

死锁示例

  • 线程A与线程B陷入无休止的阻塞状态
std::mutex mx1;
std::mutex mx2;

void thread_func1()
{
	unique_lock<std::mutex> lc1(mx1);
	cout << "1号锁" << endl;
	std::this_thread::sleep_for(std::chrono::microseconds(200));
	unique_lock<std::mutex> lc2(mx2);
	cout << "2号锁" << endl;
}


void thread_func2()
{
	unique_lock<std::mutex> lc2(mx2);
	cout << "2号锁" << endl;
	std::this_thread::sleep_for(std::chrono::microseconds(200));
	unique_lock<std::mutex> lc1(mx1);
	cout << "1号锁" << endl;
}

int main()
{
	thread tha(thread_func1);
	thread thb(thread_func2);
	
	tha.join();
	thb.join();
	return 0;
}

std::mutex mxA;
std::mutex mxB;

int countA = 1000;
int countB = 1000;

void thread_func1(int money)
{
	unique_lock<std::mutex> lcA(mxA);
	std::this_thread::sleep_for(std::chrono::milliseconds(200));
	cout << "线程1获得了A账户的锁" << endl;
	if (countA > money)
	{
		cout << "线程1请求B账户的锁子" << endl;
		unique_lock<std::mutex> lcB(mxB);
		countA -= money;
		countB += money;
		cout << "转账成功A------->B:" << money << endl;
	}
	else
	{
		cout << "A账户的余额不足" << endl;
	}
}

void thread_func2(int money)
{
	unique_lock<std::mutex> lcB(mxA);
	std::this_thread::sleep_for(std::chrono::milliseconds(200));
	cout << "线程2获得了B账户的锁" << endl;
	if (countA > money)
	{
		cout << "线程2请求A账户的锁子" << endl;
		unique_lock<std::mutex> lcA(mxB);
		countB -= money;
		countA += money;
		cout << "转账成功B------->A:" << money << endl;
	}
	else
	{
		cout << "B账户的余额不足" << endl;
	}
}


int main()
{
	thread tha(thread_func1, 200);
	thread thb(thread_func2, 300);

	tha.join();
	thb.join();

	return 0;
}

如何解决死锁

  • 条件变量进行处理
std::mutex mx;
condition_variable cv;
class Count
{
private:
	int money;
public:
	Count(int m) :money(m)
	{}
	~Count() {}

	bool operator<(const Count& src) const
	{
		return money < src.money;
	}
	int GetMoney()const { return money; }
	void SetMoney(int m) { money = m; }
};

class Account
{
public:
	~Account() = default;
	static Account& getInstance()
	{
		//static Account a; //静态变量在函数外进行实例化
		//return a;
		return acc;
	}
	void apply(Count& A, Count& B)
	{
		unique_lock<mutex> lc(mx);
		while (s.count(A) > 0 || s.count(B) > 0)//AB是否有一个存在于集合中
		{	
			cv.wait(lc);
		}
		//两个账户都不在集合中,则将两个账户加入进去
		s.insert(A);
		s.insert(B);//保证两个账户同时插入
	}
	void free(Count& A, Count& B)
	{
		unique_lock<mutex> lc(mx);
		
		s.erase(A);       
		s.erase(B);      
		cv.notify_all();
	}
private:
	Account() = default;
	Account(const Account&) = delete;
	Account& operator=(const Account&) = delete;
	std::set<Count> s;
	static Account acc;
};
Account Account::acc;//进入主函数前进行初始化

void thread_func(Count& A, Count& B, int money)
{
	Account& acc = Account::getInstance();
	acc.apply(A, B);//将AB两个账户加入
	if (A.GetMoney() >= money)
	{
		A.SetMoney(A.GetMoney() - money);
		B.SetMoney(B.GetMoney() + money);
		cout << "success ful" << endl;
		cout << money << endl;
	}
	else
	{
		cout << "余额不足" << endl;
	}
	acc.free(A, B);
}
int main()
{
	Count A(5000);
	Count B(1000);
	Count C(3000);
	Count D(4000);

	std::thread thra(thread_func, std::ref(A), std::ref(B), 500);
	std::thread thrb(thread_func, std::ref(B), std::ref(C), 300);
	std::thread thrc(thread_func, std::ref(C), std::ref(A), 500);
	std::thread thrd(thread_func, std::ref(D), std::ref(A), 300);

	thra.join();
	thrb.join();
	thrc.join();
	thrd.join();

	cout << "A: " << A.GetMoney() << endl;
	cout << "B: " << B.GetMoney() << endl;
	cout << "C: " << C.GetMoney() << endl;
	cout << "D: " << D.GetMoney() << endl;

}

我们定义了一个集合,并且在转账的过程中,要求把两个账户共同写入进去,并且进入等待队列的条件就是集合中存在两个账户

下面是使用map解决原先代码中使用set排序,导致同一个账户在insert的过程中不断的变化继而导致线程的不安全

std::mutex mx;
condition_variable cv;
class Count
{
private:
	string name;
	int money;
public:
	Count() :name(), money(0) {}
	Count(string n,int m) :name(n),money(m)	{}
	~Count() {}

	
	string& getName() { return name; }
	int GetMoney()const { return money; }
	void SetMoney(int m) { money = m; }
};

class Account
{
public:
	~Account() = default;
	static Account& getInstance()
	{
		//static Account a; //静态变量在函数外进行实例化
		//return a;
		return acc;
	}
	void apply(Count& A, Count& B)
	{
		unique_lock<mutex> lc(mx);
		while (s.count(A.getName()) > 0 || s.count(B.getName()) > 0)//AB是否有一个存在于集合中
		{	
			cv.wait(lc);
		}
		//两个账户都不在集合中,则将两个账户加入进去
		s[A.getName()] = A;
		s[B.getName()] = B;
	}
	void free(Count& A, Count& B)
	{
		unique_lock<mutex> lc(mx);
		
		s.erase(A.getName());
		s.erase(B.getName());
		cv.notify_all();
	}
private:
	Account() = default;
	Account(const Account&) = delete;
	Account& operator=(const Account&) = delete;
	std::map<string,Count> s;  //有序不重复 集合
	static Account acc;
};
Account Account::acc;//进入主函数前进行初始化

void thread_func(Count& A, Count& B, int money)
{
	Account& acc = Account::getInstance();
	acc.apply(A, B);//将AB两个账户加入
	if (A.GetMoney() >= money)
	{
		A.SetMoney(A.GetMoney() - money);
		B.SetMoney(B.GetMoney() + money);
		cout << "success ful" << endl;
		cout << money << endl;
	}
	else
	{
		cout << "余额不足" << endl;
	}
	acc.free(A, B);
}
int main()
{
	Count A("zyq",5000);
	Count B("cbq",1000);
	Count C("hxm",3000);
	Count D("scz",1500);

	std::thread thra(thread_func, std::ref(A), std::ref(B), 500);
	std::thread thrb(thread_func, std::ref(B), std::ref(C), 300);
	std::thread thrc(thread_func, std::ref(C), std::ref(A), 500);
	std::thread thrd(thread_func, std::ref(D), std::ref(A), 300);

	thra.join();
	thrb.join();
	thrc.join();
	thrd.join();

	cout << "A: " << A.GetMoney() << endl;
	cout << "B: " << B.GetMoney() << endl;
	cout << "C: " << C.GetMoney() << endl;
	cout << "D: " << D.GetMoney() << endl;
}
  • 通过对两个锁进行同时持有来解决死锁
std::mutex from;
std::mutex to;
condition_variable cv;
class Count
{
private:
	string name;
	int money;
public:
	Count() :name(), money(0) {}
	Count(string n,int m) :name(n),money(m)	{}
	~Count() {}

	
	string& getName() { return name; }
	int GetMoney()const { return money; }
	void SetMoney(int m) { money = m; }
};

class Account
{
public:
	~Account() = default;
	static Account& getInstance()
	{
		//static Account a; //静态变量在函数外进行实例化
		//return a;
		return acc;
	}
	/*
	void apply(Count& A, Count& B)
	{
		unique_lock<mutex> lc(mx);
		while (s.count(A.getName()) > 0 || s.count(B.getName()) > 0)//AB是否有一个存在于集合中
		{	
			cv.wait(lc);
		}
		//两个账户都不在集合中,则将两个账户加入进去
		s[A.getName()] = A;
		s[B.getName()] = B;
	}
	void free(Count& A, Count& B)
	{
		unique_lock<mutex> lc(mx);
		
		s.erase(A.getName());
		s.erase(B.getName());
		cv.notify_all();
	}
	*/
private:
	Account() = default;
	Account(const Account&) = delete;
	Account& operator=(const Account&) = delete;
	std::map<string,Count> s;  //有序不重复 集合
	static Account acc;
};
Account Account::acc;//进入主函数前进行初始化

void thread_func(Count& A, Count& B, int money)
{
	std::unique_lock<std::mutex> lockx(from, std::defer_lock);//暂时不进行锁 预演
	std::unique_lock<std::mutex> locky(to, std::defer_lock);  //代表持有但是不锁

	std::lock(lockx, locky);//同时获得这两个锁
	A.SetMoney(A.GetMoney() - money);
	B.SetMoney(B.GetMoney() + money);
}
int main()
{
	Count A("zyq",5000);
	Count B("cbq",1000);
	Count C("hxm",3000);
	Count D("scz",1500);

	std::thread thra(thread_func, std::ref(A), std::ref(B), 500);
	std::thread thrb(thread_func, std::ref(B), std::ref(C), 300);
	std::thread thrc(thread_func, std::ref(C), std::ref(A), 500);
	std::thread thrd(thread_func, std::ref(D), std::ref(A), 300);

	thra.join();
	thrb.join();
	thrc.join();
	thrd.join();

	cout << "A: " << A.GetMoney() << endl;
	cout << "B: " << B.GetMoney() << endl;
	cout << "C: " << C.GetMoney() << endl;
	cout << "D: " << D.GetMoney() << endl;

}
<think>好的,用户现在想了解C++死锁的成因及解决方法。之前我已经给出过关于线程死锁的详细回答,但这次需要结合用户提供的四个引用内容来调整回答,特别是C++相关的案例和解决方案。首先,我需要回顾之前的回答结构,确保符合用户的要求,比如使用中文、正确的LaTeX格式、生成相关问题等。 用户提供的引用[1]、[3]、[4]都提到了C++线程中因锁顺序不一致导致死锁问题,而引用[2]提到了层次锁和资源释放的方法。需要将这些内容整合到回答中。特别是要强调C++标准库中的工具,如std::lock和std::lock_guard的用法。 接下来,要确保回答结构清晰,分点说明成因和解决方案。成因部分需要对应四个必要条件,但可能需要用更贴近C++的例子来说明。解决方案部分需要包括锁顺序一致性、RAII管理锁、层次锁、一次性获取多个锁等,这些都是引用中提到的内容。 需要注意用户之前的回答中提到了Java代码示例,但这次需要转换为C++的示例,比如使用std::mutex和std::lock_guard。同时,用户提供的引用中有具体的代码示例,需要适当引用,比如引用[1]中的修改线程2函数来保证锁顺序,引用[3]中的死锁示例和修改后的解决方案。 还要检查是否需要添加新的解决方案,比如超时机制,这在C++中可能通过std::timed_mutex实现,但用户提供的引用中没有提到,可能需要简要提及,但主要聚焦在已有引用的解决方案上。 最后,生成的相关问题需要与C++相关,比如如何使用std::lock避免死锁C++中的RAII如何帮助预防死锁,层次锁的具体实现等。确保引用标识正确,比如引用[1]、[3]、[4]对应锁顺序和一次性获取锁,引用[2]对应层次锁和资源释放。 总结步骤: 1. 分析用户提供的引用,提取C++相关死锁案例和解决方案。 2. 结构化回答,分为成因和解决方案,每个部分结合引用中的例子。 3. 使用C++代码示例替换之前回答中的Java示例,确保语法正确。 4. 添加引用标识到对应的解决方案部分。 5. 生成相关问题,围绕C++死锁处理工具和方法。</think>### C++线程死锁的成因与解决方案 --- #### **一、死锁成因分析** C++线程死锁通常由以下四个条件共同导致,具体表现为**锁竞争顺序冲突**: 1. **互斥条件**:资源(如互斥锁`std::mutex`)同一时间只能被一个线程持有。 2. **占有且等待**:线程持有锁 A 时,尝试获取锁 B,但锁 B 被其他线程占用。 3. **不可剥夺**:无法强制抢占其他线程已持有的锁。 4. **循环等待**:多个线程形成环形等待链(如线程 1 等锁 B,线程 2 等锁 A)[^3][^4]。 --- #### **二、C++ 典型死锁场景(引用代码说明)** ##### **场景1:锁顺序不一致(引用[1][3][4])** ```cpp // 线程1:先锁mtxA,再锁mtxB void threadFunction1() { std::lock_guard<std::mutex> lockA(mtxA); std::lock_guard<std::mutex> lockB(mtxB); // 若线程2已锁mtxB,则阻塞 } // 线程2:先锁mtxB,再锁mtxA void threadFunction2() { std::lock_guard<std::mutex> lockB(mtxB); std::lock_guard<std::mutex> lockA(mtxA); // 若线程1已锁mtxA,则阻塞 } ``` **问题**:两个线程以相反顺序请求锁,导致**循环等待**。 ##### **场景2:不可重入锁(递归锁未正确使用)** 若使用非递归锁 (`std::mutex`) 实现递归调用,同一线程重复获取锁时会自我阻塞: ```cpp std::mutex mtx; void recursiveFunction(int n) { std::lock_guard<std::mutex> lock(mtx); // 第二次调用时阻塞! if (n > 0) recursiveFunction(n-1); } ``` --- #### **三、C++ 死锁解决方案** ##### **1. 统一锁顺序(破坏循环等待)** 强制所有线程**按固定顺序获取锁**(如先 A 后 B): ```cpp // 修改线程2的锁顺序(引用[1]) void threadFunction2() { std::lock_guard<std::mutex> lockA(mtxA); // 改为先锁mtxA std::lock_guard<std::mutex> lockB(mtxB); } ``` ##### **2. 使用`std::lock`一次性获取多个锁(原子操作)** 通过`std::lock`同时锁定多个互斥量,避免中间状态: ```cpp void safeFunction() { std::unique_lock<std::mutex> lockA(mtxA, std::defer_lock); std::unique_lock<std::mutex> lockB(mtxB, std::defer_lock); std::lock(lockA, lockB); // 原子操作,要么全锁,要么不锁 } ``` ##### **3. 层次锁(引用[2])** 为锁分配层级编号,线程只能请求更高层级的锁: ```cpp class HierarchicalMutex { std::mutex mtx; unsigned long hierarchy_value; public: void lock() { check_hierarchy(); // 检查当前层级是否允许获取此锁 mtx.lock(); } // 实现细节略... }; ``` ##### **4. 使用`std::recursive_mutex`(可重入锁)** 允许同一线程多次获取同一锁: ```cpp std::recursive_mutex rmtx; void recursiveFunction(int n) { std::lock_guard<std::recursive_mutex> lock(rmtx); // 可重入 if (n > 0) recursiveFunction(n-1); } ``` ##### **5. RAII 管理资源(引用[2])** 通过作用域自动释放锁,避免忘记解锁: ```cpp { std::lock_guard<std::mutex> lock(mtx); // 离开作用域自动解锁 // 操作共享资源 } ``` --- #### **四、死锁检测与调试工具** 1. **Clang ThreadSanitizer**:检测数据竞争和锁顺序问题。 2. **gdb 调试**:查看线程堆栈,分析`BLOCKED`状态的线程。 3. **日志追踪**:在锁操作前后添加日志,观察执行顺序[^3][^4]。 --- ### **总结对比表** | 方法 | 适用场景 | C++ 实现示例 | |--------------------|-----------------------------|-------------------------------| | 统一锁顺序 | 简单多锁场景 | `std::lock_guard` + 固定顺序 | | `std::lock`原子操作 | 需要同时获取多个锁 | `std::lock(mtxA, mtxB)` | | 层次锁 | 复杂层级资源管理 | 自定义`HierarchicalMutex`类 | | 可重入锁 | 递归函数或嵌套锁 | `std::recursive_mutex` | --- ### 相关问题 1. 如何在 C++ 中使用`std::lock_guard`和`std::unique_lock`避免死锁? 2. `std::recursive_mutex`与普通`std::mutex`的性能差异如何? 3. 如何通过 Clang ThreadSanitizer 检测死锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值