当前是多核时代,多线程程序十分普遍,本文来学习一下如何调试C++多线程程序。
示例代码
本文设计了一个demo多线程程序,主线程启动后会启动2个业务线程,分别执行business_1和business_2函数,这两个函数中都定义了一个计数器对象,每100毫秒计数器加1。
#include <thread>
#include <chrono>
class counter
{
public:
void add_one()
{
++count_;
}
private:
uint32_t count_{ 0 };
};
void business_1()
{
counter cnt{};
while (true) {
cnt.add_one();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void business_2()
{
counter cnt{};
while (true) {
cnt.add_one();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main()
{
std::thread t1(&business_1);
std::thread t2(&business_2);
t1.join();
t2.join();
return 0;
}
基础调试
步骤一:使用gdb运行程序
命令:gdb -tui test
步骤二:在counter类的add_one成员函数上设置断点
命令:b counter::add_one
步骤三:运行程序
命令:r
如下图所示,运行起来后,线程2先触发了上一步设置的断点。
步骤四:查看线程情况
命令:info thread
可见,此时程序有3个线程在运行,分别为:
(1)线程1是主线程,调用join子线程后,一直等待线程2、3退出;
(2)线程2是子线程,但目前无法确定是business_1还是business_2;
(3)线程3是子线程,但目前无法确定是business_1还是business_2;
其中,线程2前面有一个*,代表是当前线程。
步骤五:查看线程堆栈信息
命令:thread apply all bt
从上图可知,Thread 2运行的是business_1函数,Thread 3运行的是business_2函数。
注:也可单独可看某个线程的堆栈信息,命令如下:
(1)查看当前线程堆栈,bt
(2)查看指定线程堆栈,以Thread 2为例,运行thread apply 2 bt
步骤六:切换线程
命令:thread 线程号
假设要切换至线程3,则执行thread 3
步骤七:单步执行
命令:n
在单步执行过程中,遇到一个问题,如上图所示,由于两个子线程都在counter::add_one函数上打了断点,当单步执行当前线程时,总会触发另一个线程的断点,进而切换至另一个线程,导致线程2和线程3频繁来回切换,显然这不是我们期望的。
问题描述
在基础调试章节的步骤七中,我们遇到了在单步调试时线程频繁切换的问题,是由于两个子线程都在counter::add_one函数上打了断点,线程2单步执行时,线程3也在运行,进而线程3触发断点导致线程切换。有没有办法在调试时一直保持在当前线程不被切换呢?答案是有的,解决办法就是设置合适的scheduler-locking参数。
scheduler-locking
scheduler-locking的功能是可以在调试当前线程时控制其他线程是否可以执行。其可设置的值有:
(1)off,不锁定线程,调试时,所有线程一起运行;
(2)on,只允许当前线程执行,调试时,其他线程都不会执行;
(3)step,单步调试时,其他线程都不会执行,但执行continue、until、finish命令时,其他线程也会执行。
注:不同版本的gdb,scheduler-locking的默认值不同,可通过show scheduler-locking查看默认值,在本文示例中,默认值是off。
解决方案
了解了scheduler-locking的功能和参数之后,我们就知道了若想要在调试时当前线程不被切换,只需将scheduler-locking设置为on即可,命令如下:
(1)set scheduler-locking on – 设置
(2)show scheduler-locking – 查看设置是否成功
设置完成后,多次输入c进行调试,如下图所示,这次线程一直保持在线程2上,不再被切换了,问题得到解决。
结束语
在设计大规模应用时,通常会注重代码的可复用性,不同线程的业务会调用相同的函数,在调试时,断点打在这些通用函数上是很常见的现象。掌握了本文的调试技巧,能够帮助您在调试多线程过程中更加得心应手。