GDB 多线程之旅

658 阅读4分钟

GDB 多线程之旅

前言

首先,我来说下为什么写多线程吧!

作为一个工作几年的小老鸟,接触到的最多的编程,就是多线程编程!在多线程的世界中,每一个变量,每一把锁都是至关重要,稍微一个不小心,就会冲突或者异常;

此时,调试代码就是重中之重了!Windows就不说了,主要有界面,打断点,查变量等等操作简直容易的不要不要的了!Linux下的多线程调试就比较骚气了,由于没有界面的“光环”加持,所以我们必须要借助强大的辅助工具----GDB,熟练使用GDB调试程序,是一个linux程序员必备的技能之一;

这里我就总结和回顾一下linux环境下GDB调试多线程的一个过程~

GDB 线程调试命令

常用线程调试命令:

info threads 查看当前进程的线程thread <ID>  切换调试的线程为指定ID的线程break test.c:100 thread all   在所有线程中相应的行上设置断点set scheduler-locking off|on  off   默认值,不锁定任何线程,所有线程都执行  on    只有当前被调试程序会执行

这几个调试命令基本是在线程调试中经常用,也比较简单;

接下来,我们实战练习一下:

实战

编程多线程代码,目前使用两个线程进行测试:

#include <iostream>#include <thread>#include <mutex>#include <unistd.h>using namespace std;mutex _mutex;static int total = 0;//创建一个函数,供多个线程调用void fun(int num){    while(1)    {        sleep(2);        lock_guard<mutex>lock(_mutex);        total +=num;        cout<<"num:"<<num<<endl;    }}int main(){    thread t1(fun,3);    thread t2(fun,5);    t1.join();    t2.join();    return 0;}

编译:

g++ -std=c++11 testthread.cpp -o testthread -lpthread -g

然后进行GDB调试界面:

gdb testthread

出现调试界面:

root@iZuf67on1pthsuih96udyfZ:~/GDB/test20200727# gdb test1GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1Copyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.  Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.root@iZuf67on1pthsuih96udyfZ:~/GDB/test20200730# lstestthread  testthread.cpproot@iZuf67on1pthsuih96udyfZ:~/GDB/test20200730# gdb testthreadGNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1

打断点,运行程序

(gdb) l 17
12      void fun(int num)
13      {
14          while(1)
15          {
16              sleep(2);
17              lock_guard<mutex>lock(_mutex);
18              total +=num;
19              cout<<"num:"<<num<<endl;
20          }
21      }
(gdb) b 17
Breakpoint 1 at 0x40117b: file testthread.cpp, line 17.
(gdb) run
Starting program: /root/GDB/test20200730/testthread 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff6f4e700 (LWP 15195)]
[New Thread 0x7ffff674d700 (LWP 15196)]
[Switching to Thread 0x7ffff6f4e700 (LWP 15195)]

Thread 2 "testthread" hit Breakpoint 1, fun (num=3) at testthread.cpp:17
17              lock_guard<mutex>lock(_mutex);
(gdb) 

查看下线程:

(gdb) info threads
  Id   Target Id         Frame 
  1    Thread 0x7ffff7fe3740 (LWP 15191) "testthread" 0x00007ffff7bc298d in pthread_join (threadid=140737336633088, thread_return=0x0) at pthread_join.c:90
* 2    Thread 0x7ffff6f4e700 (LWP 15195) "testthread" fun (num=3) at testthread.cpp:17
  3    Thread 0x7ffff674d700 (LWP 15196) "testthread" fun (num=5) at testthread.cpp:17
(gdb) 

说明出现两条线程,目前执行的是线程ID是2

我们可以使用GDB命令去切换线程

(gdb) thread 3[Switching to thread 3 (Thread 0x7ffff674d700 (LWP 15196))]#0  fun (num=5) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);

我们很自然的回发现,线程都会在我们断点处停止,而且我们线程也已经切换完成;

我们一步一步的看会出现什么现象:

(gdb) thread 3[Switching to thread 3 (Thread 0x7ffff674d700 (LWP 15196))]#0  fun (num=5) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);(gdb) nnum:3Thread 3 "testthread" hit Breakpoint 1, fun (num=5) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);(gdb) n[Switching to Thread 0x7ffff6f4e700 (LWP 15195)]Thread 2 "testthread" hit Breakpoint 1, fun (num=3) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);(gdb) nnum:518              total +=num;(gdb) n19              cout<<"num:"<<num<<endl;

从调试信息可以观察到,我们一步步调试的时候,另外一个线程也会出现在调试信息中

这里我们使用命令,我们只使用单条线程进行调试操作

(gdb) set scheduler-locking on(gdb) (gdb) info threads  Id   Target Id         Frame   1    Thread 0x7ffff7fe3740 (LWP 15278) "testthread" 0x00007ffff7bc298d in pthread_join (threadid=140737336633088, thread_return=0x0) at pthread_join.c:90* 2    Thread 0x7ffff6f4e700 (LWP 15279) "testthread" fun (num=3) at testthread.cpp:17  3    Thread 0x7ffff674d700 (LWP 15280) "testthread" fun (num=5) at testthread.cpp:17(gdb) n18              total +=num;(gdb) p total$1 = 0(gdb) n19              cout<<"num:"<<num<<endl;(gdb) p total$2 = 3(gdb) nnum:317              lock_guard<mutex>lock(_mutex);(gdb) n14          while(1)(gdb) n16              sleep(2);(gdb) nThread 2 "testthread" hit Breakpoint 1, fun (num=3) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);(gdb) n18              total +=num;(gdb) p total$3 = 3(gdb) n19              cout<<"num:"<<num<<endl;(gdb) p total$4 = 6(gdb) 

通过调试信息可以知道,我们发现了,设置了单个线程执行的时候,只有当前线程在执行,我们执行到加锁的上一句,切换一下线程,再试下,看一下会不会出现我们想要的结果:

(gdb) thread 3[Switching to thread 3 (Thread 0x7ffff674d700 (LWP 15280))]#0  fun (num=5) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);(gdb) nThread 3 "testthread" hit Breakpoint 1, fun (num=5) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);(gdb) n18              total +=num;(gdb) p total$5 = 9(gdb) n19              cout<<"num:"<<num<<endl;(gdb) nnum:517              lock_guard<mutex>lock(_mutex);(gdb) n14          while(1)(gdb) n16              sleep(2);(gdb) nThread 3 "testthread" hit Breakpoint 1, fun (num=5) at testthread.cpp:1717              lock_guard<mutex>lock(_mutex);(gdb) n18              total +=num;(gdb) p total$6 = 14(gdb) 

我们会很惊奇的发现,切换到线程3的时候,只有只有线程3在执行,这样极大便利的让我们调试多线程代码就像调试单线程一样,不得不感叹一句呀!GDB - -真的强大~

总结

linux下使用GDB是程序员必备的技能之一,多线程的调试更是重中之重,所以我们在工作和学习中,多对代码进行调试运行,这样不仅可以提高自己的调试代码能力,而且可以帮助我们更好的去理解代码的逻辑。

在前进的道路上,我们一起加油~

往期精彩文章汇总

  • [muduo源码剖析学习总结](https://mp.weixin.qq.com/s/HpCH4_5Mw13GoUqm4JyrGQ)
  • [掌握这个技能,你可以畅游github](https://mp.weixin.qq.com/s/5gUVvIMOyxgOsEZgkzoBMw)
  • [C++ 简单对象池实现](https://mp.weixin.qq.com/s/EH059KquO_RiFdM6WMcnmQ)
  • [内存池设计与实现](https://mp.weixin.qq.com/s/gb5FMlKg4HK_fGiqgYKzgA)
  • [恭喜你!发现宝藏一份--技术文章汇总](https://mp.weixin.qq.com/s/H-OKvMkzjh7E3R8JhThRew)

想了解学习更多C++后台服务器方面的知识,请关注: 微信公众号:====后台服务器开发====

冰冻三尺,非一日之寒,水滴石穿,非一日之功,愿我们一起加油努力~