代码调试工具gdb是一个能够让我们在工作中高效排查代码异常根源的利器。
在此将gdb针对多线程的调试方式做一个笔记,也方便后续回顾以及分享大家。
本文采用的是一个简单的多线程代码示例,同时调试是在mac上进行的
mac安装gdb
brew install gdb
即可
基本命令介绍
开始之前先简单介绍几个gdb调试多线程的子命令
- layout next 开启子窗口,显示当前程序运行所在的源码位置
- b xxx 在某一行,或者某一个函数处增加断点
- info b 查看断点信息
- r 运行程序
- info threads 查看当前可调试的所有线程信息
- where 查看进程运行到当前位置的函数调用栈信息
- thread num 调试状态切换到线程num
- break thread_test.cc:123 thread all 在所有线程中相应的行上设置断点
- thread apply all id1 id2 command 让一个或者多个线程执行GDB命令
- thread apply all command 让所有被调试线程执行gdb命令 command
- watch variable 在断点当前位置增加变量的variable的监控信息,后续运行的时候会打印
- c 继续运行,直到遇到断点停顿
示例1
目标:在不加锁的情况下,多个线程的运行交叉运行的,导致进程内部共享的变量对外的体现不是连续的。
代码如下
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
static int g_a = 0;
void pthread_func1() {
for (int i = 1;i < 5000; ++i) {
g_a ++;
}
}
void pthread_func2() {
for (int i = 5000;i < 10000; ++i) {
g_a ++;
}
}
int main() {
thread t1(pthread_func1);
thread t2(pthread_func2);
t1.join();
t2.join();
return 0;
}
编译:
g++ -std=c++11 -g pthread_test.cc -o pthread_test -pthread
调试过程如下:
通过以上调试我们可以发现在当前进程中的变量g_a每运行一次并不是累加1,可能会累加2,而且代码的跳转也能发现其实是多个线程交替执行。
基本的调试方式也就是使用我们已经说过的线程相关调试命令了。
示例2
目标:在加锁的情况下,我们的进程全局变量的访问变成了串行方式
测试代码如下:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex g_lock;
static int g_a = 0;
void pthread_func1() {
for (int i = 1;i < 5000; ++i) {
g_lock.lock();
g_a ++;
g_lock.unlock();
}
}
void pthread_func2() {
for (int i = 5000;i < 10000; ++i) {
g_lock.lock();
g_a ++;
g_lock.unlock();
}
}
int main() {
thread t1(pthread_func1);
thread t2(pthread_func2);
t1.join();
t2.join();
return 0;
}
编译调试过程如下: