Linux多线程编程: 死锁场景、原因分析、解决方案

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 本文目标

列举各种pthread编程的死锁场景,并简要分析原因,之后给出(不一定最优的)解决方案。

3. 死锁场景

3.1 案例1

3.1.1 场景

多个线程共享锁,然后再某个线程发***pthread_kill()/pthread_cancel()***调用,造成的死锁。代码如下:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

void *thread_func(void *arg)
{
	printf("thread started\n");
	
	for (;;) {
		pthread_mutex_lock(&mtx);
		printf("thread: lock\n");
		sleep(10);
		pthread_mutex_unlock(&mtx);
		printf("thread: unlock\n");
	}
}


int main(void)
{
	pthread_t pth;

	if (pthread_create(&pth, NULL, thread_func, NULL)) {
		perror("pthread_create");
		return -1;
	}

	sleep(3); /* 延时一段时间,保证 pth 比主线程先拿到锁 */

	printf("stop the thread... ");
	pthread_cancel(pth);
	pthread_join(pth, NULL);
	printf("done.\n");

	printf("main thread: try to get lock...\n");
	pthread_mutex_lock(&mtx);
	printf("main thread: do something with lock\n");
	pthread_mutex_unlock(&mtx);


	printf("main thread: exit\n");
	
	return 0;
}

# 运行结果
$ ./pthread_deadlock 
thread started
thread: lock
stop the thread... done.
main thread: try to get lock...

可以看到主线程将永远无法等到锁。

3.1.2 原因分析

如果程序没有设置可在任意点退出(可通过pthread_setcanceltype()设置),pthread_kill()/pthread_cancel() 调用,给目标线程发停止信号,导致目标线程在cancellation point退出。什么是cancellation point? 简单来说,就是给线程发停止信号时,线程退出的位置。可通过 man 7 pthreads 查询,哪些库函数是是一个cancellation point。
https://man7.org/linux/man-pages/man7/pthreads.7.html
在文档中搜索关键字"Cancellation points",从列表看到,很不幸,sleep()函数就是一个cancellation point,意味着线程可能在该处退出,此时会导致共享锁没有释放,主线程无法获得该锁,造成死锁。

3.1.3 解决方案

我们可以为所有的共享锁,维护一个每线程的锁嵌套层次计数:上锁时,嵌套计数加1,当计数由0变1时,调用pthread_setcancelstate()禁用线程的cancel;锁释放时,嵌套层次减1,当嵌套层次减到0时,我们调用pthread_testcancel(),该函数检测是否有挂起的***phread_cancel()/pthread_kill()***请求,有则会退出线程。
该方案有着很明显的缺点,当在锁内处理的事务耗时较长时,线程的退出将会延迟较长时间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值