GDB 多线程之旅

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 test1
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (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# ls
testthread  testthread.cpp
root@iZuf67on1pthsuih96udyfZ:~/GDB/test20200730# gdb testthread
GNU 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:17
17              lock_guard<mutex>lock(_mutex);

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

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

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

Thread 3 "testthread" hit Breakpoint 1, fun (num=5) at testthread.cpp:17
17              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:17
17              lock_guard<mutex>lock(_mutex);
(gdb) n
num:5
18              total +=num;
(gdb) n
19              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) n
18              total +=num;
(gdb) p total
$1 = 0
(gdb) n
19              cout<<"num:"<<num<<endl;
(gdb) p total
$2 = 3
(gdb) n
num:3
17              lock_guard<mutex>lock(_mutex);
(gdb) n
14          while(1)
(gdb) n
16              sleep(2);
(gdb) n

Thread 2 "testthread" hit Breakpoint 1, fun (num=3) at testthread.cpp:17
17              lock_guard<mutex>lock(_mutex);
(gdb) n
18              total +=num;
(gdb) p total
$3 = 3
(gdb) n
19              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:17
17              lock_guard<mutex>lock(_mutex);
(gdb) n

Thread 3 "testthread" hit Breakpoint 1, fun (num=5) at testthread.cpp:17
17              lock_guard<mutex>lock(_mutex);
(gdb) n
18              total +=num;
(gdb) p total
$5 = 9
(gdb) n
19              cout<<"num:"<<num<<endl;
(gdb) n
num:5
17              lock_guard<mutex>lock(_mutex);
(gdb) n
14          while(1)
(gdb) n
16              sleep(2);
(gdb) n

Thread 3 "testthread" hit Breakpoint 1, fun (num=5) at testthread.cpp:17
17              lock_guard<mutex>lock(_mutex);
(gdb) n
18              total +=num;
(gdb) p total
$6 = 14
(gdb) 

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


总结

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

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



往期精彩文章汇总


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


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值