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++后台服务器方面的知识,请关注: 微信公众号:====后台服务器开发====
冰冻三尺,非一日之寒,水滴石穿,非一日之功,愿我们一起加油努力~