gdb调试C++多线程程序

当前是多核时代,多线程程序十分普遍,本文来学习一下如何调试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上,不再被切换了,问题得到解决。
解决问题

结束语

在设计大规模应用时,通常会注重代码的可复用性,不同线程的业务会调用相同的函数,在调试时,断点打在这些通用函数上是很常见的现象。掌握了本文的调试技巧,能够帮助您在调试多线程过程中更加得心应手。

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PerfMan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值