口碑好的定制网站建设,做网站后端用户用什么写,东莞免费网站建设网络营销,动态域名申请在多线程开发过程中很多人应该都会遇到死锁问题#xff0c;死锁问题也是面试过程中经常被问到的问题#xff0c;这里介绍在c中如何使用gdbpython脚本调试死锁问题#xff0c;以及如何在程序运行过程中检测死锁。首先介绍什么是死锁#xff0c;看下维基百科中的定义#xf… 在多线程开发过程中很多人应该都会遇到死锁问题死锁问题也是面试过程中经常被问到的问题这里介绍在c中如何使用gdbpython脚本调试死锁问题以及如何在程序运行过程中检测死锁。首先介绍什么是死锁看下维基百科中的定义死锁(英语Deadlock)又译为死结计算机科学名词。当两个以上的运算单元双方都在等待对方停止运行以获取系统资源但是没有一方提前退出时就称为死锁。在多任务操作系统中操作系统为了协调不同行程能否获取系统资源时为了让系统运作必须要解决这个问题。维基百科中介绍的是进程死锁多线程中也会产生死锁一样的道理这里不作过多介绍。死锁的四个条件禁止抢占(no preemption)系统资源不能被强制从一个进程(线程)中退出已经获得的资源在未使用完之前不能被抢占。等待和保持(hold and wait)一个进程(线程)因请求资源阻塞时对已获得的资源保持不放。互斥(mutual exclusion)资源只能同时分配给一个进程(线程)无法多个进程(线程)共享。循环等待(circular waiting)一系列进程(线程)互相持有其他进程(线程)所需要的资源。只有同时满足以上四个条件才会产生死锁想要消除死锁只需要破坏其中任意一个条件即可。如何调试多线程死锁问题多线程出现死锁的大部分原因都是因为多个线程中加锁的顺序不一致导致的看如下这段会出现死锁的代码using std::cout;std::mutex mutex1;std::mutex mutex2;std::mutex mutex3;void FuncA() { std::lock_guardstd::mutex guard1(mutex1); std::this_thread::sleep_for(std::chrono::seconds(1)); std::lock_guardstd::mutex guard2(mutex2); std::this_thread::sleep_for(std::chrono::seconds(1));}void FuncB() { std::lock_guardstd::mutex guard2(mutex2); std::this_thread::sleep_for(std::chrono::seconds(1)); std::lock_guardstd::mutex guard3(mutex3); std::this_thread::sleep_for(std::chrono::seconds(1));}void FuncC() { std::lock_guardstd::mutex guard3(mutex3); std::this_thread::sleep_for(std::chrono::seconds(1)); std::lock_guardstd::mutex guard1(mutex1); std::this_thread::sleep_for(std::chrono::seconds(1));}int main() { std::thread A(FuncA); std::thread B(FuncB); std::thread C(FuncC); std::this_thread::sleep_for(std::chrono::seconds(5)); if (A.joinable()) { A.join(); } if (B.joinable()) { B.join(); } if (C.joinable()) { C.join(); } cout hello\n; return 0;}如图线程A已经持有mutex1想要申请mutex2拿到mutex2后才可以释放mutex1和mutex2而此时mutex2被线程B占用。线程B已经持有mutex2想要申请mutex3拿到mutex3后才可以释放mutex2和mutex3而此时mutex3被线程C占用。线程C已经持有mutex3想要申请mutex1拿到mutex1后才可以释放mutex3和mutex1而此时mutex1被线程A占用。三个线程谁也不让着谁导致了死锁。传统gdb调试多线程死锁方法(1)attach id关联到发生死锁的进程id(gdb) attach 109Attaching to process 109[New LWP 110][New LWP 111][New LWP 112][Thread debugging using libthread_db enabled]Using host libthread_db library /lib/x86_64-linux-gnu/libthread_db.so.1.0x00007fa33f9e8d2d in __GI___pthread_timedjoin_ex (threadid140339109693184, thread_return0x0, abstime0x0,blockout) at pthread_join_common.c:89 pthread_join_common.c: No such file or directory.(2)info threads查看当前进程中所有线程的信息也可以查看到部分堆栈信息(gdb) info threadsId Target Id Frame* 1 Thread 0x7fa33ff10740 (LWP 109) out 0x00007fa33f9e8d2d in __GI___pthread_timedjoin_ex (threadid140339109693184, thread_return0x0, abstime0x0, block) at pthread_join_common.c:892 Thread 0x7fa33ec80700 (LWP 110) out __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:1353 Thread 0x7fa33e470700 (LWP 111) out __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:1354 Thread 0x7fa33dc60700 (LWP 112) out __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135这里可以看到2、3、4线程都在lock_wait状态基本上可以看出或许是否问题但是不一定这里需要多次info threads看看这些线程有没有什么变化多次如果都没有变化那基本上就是发生了死锁。(3)thread id进入具体线程(gdb) thread 2[Switching to thread 2 (Thread 0x7fa33ec80700 (LWP 110))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S: No such file or directory.(4)bt查看当前线程堆栈信息(gdb) bt#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135#1 0x00007fa33f9ea023 in __GI___pthread_mutex_lock (mutex0x7fa340204180 ) at ../nptl/pthread_mutex_lock.c:78#2 0x00007fa340000fff in __gthread_mutex_lock(pthread_mutex_t*) ()#3 0x00007fa3400015b2 in std::mutex::lock() ()#4 0x00007fa3400016d8 in std::lock_guard:mutex::lock_guard(std::mutex) ()#5 0x00007fa34000109b in FuncA() ()#6 0x00007fa340001c07 in void std::__invoke_impl(std::__invoke_other, void (*)()) ()调试到这里基本已经差不多了针对pthread_mutex_t却可以打印出被哪个线程持有之后再重复步骤3和4就可以确定哪几个线程以及哪几把锁发生的死锁而针对于std::mutexgdb没法打印具体的mutex的信息不能看出来mutex是被哪个线程持有只能依次进入线程查看堆栈信息。然而针对于c11的std::mutex有没有什么好办法定位死锁呢有。可以算作第五步继续(5)source加载deadlock.py脚本(gdb) source -v deadlock.pyType deadlock to detect deadlocks.(6)输入deadlock检测死锁(gdb) deadlock[Switching to thread 3 (Thread 0x7f5585670700 (LWP 123))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 in ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S[Switching to thread 4 (Thread 0x7f5584e60700 (LWP 124))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 in ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S[Switching to thread 2 (Thread 0x7f5585e80700 (LWP 122))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 in ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S#1 0x00007f5586bea023 in __GI___pthread_mutex_lock (mutex0x7f5587404180 ) at ../nptl/pthread_mutex_lock.c:78[Switching to thread 3 (Thread 0x7f5585670700 (LWP 123))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135#1 0x00007f5586bea023 in __GI___pthread_mutex_lock (mutex0x7f55874041c0 ) at ../nptl/pthread_mutex_lock.c:78[Switching to thread 4 (Thread 0x7f5584e60700 (LWP 124))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135#1 0x00007f5586bea023 in __GI___pthread_mutex_lock (mutex0x7f5587404140 ) at ../nptl/pthread_mutex_lock.c:78Found deadlock!Thread 2 (LWP 122) is waiting on pthread_mutex_t (0x00007f5587404180) held by Thread 3 (LWP 123)Thread 3 (LWP 123) is waiting on pthread_mutex_t (0x00007f55874041c0) held by Thread 4 (LWP 124)Thread 4 (LWP 124) is waiting on pthread_mutex_t (0x00007f5587404140) held by Thread 2 (LWP 122)直接看结果脚本检测出了死锁并指明了具体的哪几个线程造成的死锁根据输出信息可以明显看出来线程锁形成的环造成了死锁找到了具体是哪几个线程构成的死锁环就可以查看相应线程的堆栈信息查看到哪几把锁正在等待。死锁检测脚本的原理还是拿上面图举例线程A已经持有mutex1想要申请mutex2拿到mutex2后才可以释放mutex1和mutex2而此时mutex2被线程B占用。线程B已经持有mutex2想要申请mutex3拿到mutex3后才可以释放mutex2和mutex3而此时mutex3被线程C占用。线程C已经持有mutex3想要申请mutex1拿到mutex1后才可以释放mutex3和mutex1而此时mutex1被线程A占用。如图三个线程形成了一个环死锁检测就是检查线程之间是否有环的存在。单独检查死锁的环比较容易这里延申下还涉及到简单环的概念因为正常检测出来的环可能是个大环不是权值顶点数最少的环如果检测的环的顶点数较多加大定位的代价脚本就是检测的简单环这里涉及到强连通分量算法和简单环算法比较繁琐就不过多介绍了脚本来源于facebook的folly库(这里推荐看下google的abseil和facebook的folly都是好东西)代码较长在文中不好列出如果有需要的话可以自行下载或者关注加我好友发给你。如何在代码中检测死锁和上面介绍的原理相同在线程加锁过程中始终维护一张图记录线程之间的关系A-B, B-C, C-A然后在图中检出简单环找到哪几个线程处在死锁状态做好状态记录就可以完备确认是具体是哪几个线程哪几把锁发生的死锁代码同样比较长可以关注加我好友哦~REVIEW◆◆往期回顾一文让你搞懂设计模式RAII妙用之计算函数耗时深入浅出虚拟内存C线程池的实现之格式修订版关于GDB你需要知道的技巧