如何寻找多线程程序的bug

1. 准备工作

确保程序以单线程方式运行时没有错误。这是找bug的基础。

2. 区分错误类型

2.1 根本无法运行:

可能原因1:对mutex使用lock后忘记解锁
例如:没有对每一种可能的情况进行unlock。

mutex m1;

thread1{
	m1.lock();
	if(......){
		return;
	}
	m1.unlock();
	return;
}

可能原因2:发生死锁
例如:使用普通锁对递归程序加锁。

mutex m1;

void func1(){
	m1.lock();
	......
	......
	fun1();
	......
	......
	m1.unlock();
}

2.2 运行过程中异常终止:

可能原因1:数据竞争导致的数组越界错误。
例如:

atomic<int> index = 0;
vector<int> vec;

int func(int n){
	......
	......
}

thread1{
	index++;
	vec.push_back(func(index));
	vec.push_back(func(index+1));
	vec.push_back(func(index+2));
	......
	......
	......
	cout << vec[index*3-3] << endl;
	cout << vec[index*3-2] << endl;
	cout << vec[index*3-1] << endl;
}

thread2{
	index++;
	vec.push_back(func(index));
	vec.push_back(func(index+1));
	vec.push_back(func(index+2));
	......
	......
	......
	cout << vec[index*3-3] << endl;
	cout << vec[index*3-2] << endl;
	cout << vec[index*3-1] << endl;
}

2.3 正常运行但结果错误:

可能原因:数据竞争,没有对共享的全局变量做有效保护。
举个例子:

atomic<int> index = 0;

thread1{
	index++;
	int a = index;
	cout << a << endl;
}

thread2{
	index++;
	int a = index;
	cout << a << endl;
}

2.4 正常运行,但结果偶尔错误:

即使保证输入数据完全不变,结果也会出现时对时错的情况。这种问题基本上不会出现在单线程程序中。

可能原因:仍然是数据竞争,但与2.3的区别在于,大部分时候程序运算的结果都是正确的。我们知道,数据竞争这类多线程错误,往往发生在一个线程读完共享变量但没来得及写时,另一个线程闯入并修改共享变量导致的。所以这样的小概率错误就提醒我们,线程中对某个共享变量的读和写没有得到保护,但由于读操作和写操作的位置非常近,使得其他线程恰好在读和写的间隔中修改了共享变量的概率很低。

举个例子:

atomic<int> index = 0;

thread1{
	index++;
	int a = index;
	/*
	占99%以上运行时间的其他代码
	*/
}

thread2{
	index++;
	int a = index;
	/*
	占99%以上运行时间的其他代码
	*/
}

其实可以看到,这仍然是一个很普通的多线程错误。但是两个线程下其他代码占用99%以上的时间,这就导致恰好在index++;int a = index;之间切换线程的概率很低。

3. 缩小错误范围

如果第2步仍然没有帮你找到程序的错误所在,那么就不得不使用比较麻烦的方法,一步步缩小错误范围。

3.1 只用一个线程的程序

将最大线程数设置为1,测试是否会发生错误。如果会发生错误,说明bug出现在多线程程序额外引入的各种原子变量或者多线程容器上,而不是数据竞争。

3.2 抽取中间结果

在每两个步骤之间抽取出中间结果,与正确程序的中间结果进行对比。如果一样说明没有问题。
注意:在某些程序中,使用多线程时中间结果不一样反而是正常的。

4. 总结

结合2与3中方法,应该能大致定位出bug所在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值