<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何調(diào)試多線程程序

          共 1402字,需瀏覽 3分鐘

           ·

          2020-07-18 17:14

          在上一篇文章《使用 gdb 調(diào)試多進程程序 —— 以調(diào)試 nginx 為例》我們介紹了如何使用 gdb 調(diào)試多進程程序,這篇文章我們來介紹下如何使用 gdb 調(diào)試多線程程序,同時這個方法也是我閱讀和分析一個新的 C/C++ 項目常用的方法。

          當(dāng)然,多線程調(diào)試的前提是你需要熟悉多線程的基礎(chǔ)知識,包括線程的創(chuàng)建和退出、線程之間的各種同步原語等。如果您還不熟悉多線程編程的內(nèi)容,可以參考這個專欄《C++ 多線程編程專欄》,如果您不熟悉 gdb 調(diào)試可以參考這個專欄《Linux GDB 調(diào)試教程》。

          一、調(diào)試多線程的方法

          使用 gdb 將程序跑起來,然后按 Ctrl + C 將程序中斷下來,使用 info threads 命令查看當(dāng)前進程有多少線程。

          a288bd78049d7a4c4ffc0881f3234e6b.webp


          還是以 redis-server 為例,當(dāng)使用 gdb 將程序運行起來后,我們按 Ctrl + C 將程序中斷下來,此時可以使用 info threads 命令查看 redis-server 有多少線程,每個線程正在執(zhí)行哪里的代碼。

          使用 thread 線程編號 可以切換到對應(yīng)的線程去,然后使用 bt 命令可以查看對應(yīng)線程從頂?shù)降讓拥暮瘮?shù)調(diào)用,以及上層調(diào)用下層對應(yīng)的源碼中的位置;當(dāng)然,你也可以使用 frame 棧函數(shù)編號(棧函數(shù)編號即下圖中的 #0 ~ #4,使用 frame 命令時不需要加 #)切換到當(dāng)前函數(shù)調(diào)用堆棧的任何一層函數(shù)調(diào)用中去,然后分析該函數(shù)執(zhí)行邏輯,使用 print 等命令輸出各種變量和表達式值,或者進行單步調(diào)試。

          1a3c872e43706ff49ca8d48cfa89184d.webp


          如上圖所示,我們切換到了 redis-server 的 1 號線程,然后輸入 bt 命令查看該線程的調(diào)用堆棧,發(fā)現(xiàn)頂層是 main 函數(shù),說明這是主線程,同時得到從 main 開始往下各個函數(shù)調(diào)用對應(yīng)的源碼位置,我們可以通過這些源碼位置來學(xué)習(xí)研究調(diào)用處的邏輯。對每個線程都進行這樣的分析之后,我們基本上就可以搞清楚整個程序運行中的執(zhí)行邏輯了。

          接著我們分別通過得到的各個線程的線程函數(shù)名去源碼中搜索,找到創(chuàng)建這些線程的函數(shù)(下文為了敘述方便,以 f 代稱這個函數(shù)),再接著通過搜索 f 或者給 f 加斷點重啟程序看函數(shù) f 是如何被調(diào)用的,這些操作一般在程序初始化階段。

          redis-server 1 號線線程是在 main 函數(shù)中創(chuàng)建的,我們再看下 2 號線程的創(chuàng)建,使用 thread 2 切換到 2號線程,然后使用 bt 命令查看 2 號線程的調(diào)用堆棧,得到 2 號線程的線程函數(shù)為 bioProcessBackgroundJobs,注意在頂層的 clonestart_thread 是系統(tǒng)函數(shù),我們找的線程函數(shù)應(yīng)該是項目中的自定義線程函數(shù)。

          ee4eea5530d10bb9d1387006946907f4.webp

          通過在項目中搜索 bioProcessBackgroundJobs 函數(shù),我們發(fā)現(xiàn) bioProcessBackgroundJobs 函數(shù)在 bioInit 中被調(diào)用,而且確實是在 bioInit 函數(shù)中創(chuàng)建了線程 2,因此我們看到了 pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) 這樣的調(diào)用。
           1//bio.c?96行
          2void?bioInit(void)?{
          3????//...省略部分代碼...
          4
          5????for?(j?=?0;?j? 6????????void?*arg?=?(void*)(unsigned?long)?j;
          7????????//在這里創(chuàng)建了線程?bioProcessBackgroundJobs
          8????????if?(pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg)?!=?0)?{
          9????????????serverLog(LL_WARNING,"Fatal:?Can't?initialize?Background?Jobs.");
          10????????????exit(1);
          11????????}
          12????????bio_threads[j]?=?thread;
          13????}
          14}

          此時,我們可以繼續(xù)在項目中查找 bioInit 函數(shù),看看它在哪里被調(diào)用的,或者直接給 bioInit 函數(shù)加上斷點,然后重啟 redis-server,等斷點觸發(fā),使用 bt 命令查看此時的調(diào)用堆棧就知道 bioInit 函數(shù)在何處調(diào)用的了。

           1(gdb)?b?bioInit
          2Breakpoint?1?at?0x498e5e:?file?bio.c,?line?103.
          3(gdb)?r
          4The?program?being?debugged?has?been?started?already.
          5Start?it?from?the?beginning??(y?or?n)?y
          6Starting?program:?/root/redis-6.0.3/src/redis-server?
          7[Thread?debugging?using?libthread_db?enabled]
          8//...省略部分無關(guān)輸出...
          9Breakpoint?1,?bioInit?()?at?bio.c:103
          10103?????????for?(j?=?0;?j?11(gdb)?bt
          12#0??bioInit?()?at?bio.c:103
          13#1??0x0000000000431b5d?in?InitServerLast?()?at?server.c:2953
          14#2??0x000000000043724f?in?main?(argc=1,?argv=0x7fffffffe318)?at?server.c:5142
          15(gdb)?

          至此我們發(fā)現(xiàn) 2 號線程是在 main 函數(shù)中調(diào)用了 InitServerLast 函數(shù),后者又調(diào)用 bioInit 函數(shù),然后在 bioInit 函數(shù)中創(chuàng)建了新的線程 bioProcessBackgroundJobs,我們只要分析這個執(zhí)行流就能搞清楚這個邏輯流程了。

          同樣的道理,redis-server 還有 3 號和 4 號線程,我們也可以按分析 2 號線程的方式去分析 3 號和 4號,讀者可以按照這里介紹的方法。

          以上就是我閱讀一個不熟悉的 C/C++ 項目常用的方法,當(dāng)然對于一些特殊的項目的源碼,你還需要去了解一下該項目的的業(yè)務(wù)內(nèi)容,否則除了技術(shù)邏輯以外,你可能需要一些業(yè)務(wù)知識才能看懂各個線程調(diào)用棧以及初始化各個線程函數(shù)過程中的業(yè)務(wù)邏輯。

          二、調(diào)試時控制線程切換

          在調(diào)試多線程程序時,有時候我們希望執(zhí)行流一直在某個線程執(zhí)行,而不是切換到其他線程,有辦法做到這樣嗎?

          為了說明清楚這個問題,我們假設(shè)現(xiàn)在調(diào)試的程序有 5 個線程,除了主線程,其他 4 個工作線程的線程函數(shù)都是下面這樣一個函數(shù):

           1void*?worker_thread_proc(void*?arg)
          2
          {
          3????while?(true)
          4????{
          5????????//代碼行1
          6????????//代碼行2
          7????????//代碼行3
          8????????//代碼行4
          9????????//代碼行5
          10????????//代碼行6
          11????????//代碼行7
          12????????//代碼行8
          13????????//代碼行9
          14????????//代碼行10
          15????????//代碼行11
          16????????//代碼行12
          17????????//代碼行13
          18????????//代碼行14
          19????????//代碼行15
          20????}??
          21}

          為了方便表述,我們把四個工作線程分別叫做 A、B、C、D。

          f7ba2475cc0bb6dad736a19f017308b0.webp

          如上圖所示,假設(shè)某個時刻, 線程 A 的停在代碼行 3 處,線程 B、C、D 停留位置代碼行 1 ~15 任一位置,此時線程 A 是 gdb 當(dāng)前調(diào)試線程,此時我們輸入 next 命令,期望調(diào)試器跳轉(zhuǎn)到代碼行 4 處;或者輸入 util 10 命令,期望調(diào)試器跳轉(zhuǎn)到**代碼行 10 **處。但是實際情況下,如果代碼行 1代碼行 2、代碼行 13 或者代碼行 14 處設(shè)置了斷點,gdb 再次停下來的時候,可能會停在到代碼行 1 、代碼行 2 、代碼行 13代碼行 14 這樣的地方。

          這是多線程程序的特點:當(dāng)我們從代碼行 4 處讓程序繼續(xù)運行時,線程 A 雖然會繼續(xù)往下執(zhí)行,下一次應(yīng)該在代碼行 14 處停下來,但是線程 B、C、D 也在同步運行呀,如果此時系統(tǒng)的線程調(diào)度將 CPU 時間片切換到線程 B、C 或者 D 呢?那么 gdb 最終停下來的時候,可能是線程 B、CD ?觸發(fā)了 代碼行 1 、代碼行 2代碼行 13、代碼行 14 處的斷點,此時調(diào)試的線程會變?yōu)?B、C 或者 D,而此時打印相關(guān)的變量值,可能就不是我們期望的線程 A 函數(shù)中的相關(guān)變量值了。

          還存在一個情況,我們單步調(diào)試線程 A 時,我們不希望線程 A 函數(shù)中的值被其他線程改變。

          針對調(diào)試多線程存在的上述狀況,gdb 提供了一個在調(diào)試時將程序執(zhí)行流鎖定在當(dāng)前調(diào)試線程的命令選項——scheduler-locking 選項,這個選項有三個值,分別是 on、step 和 off,使用方法如下:

          1set?scheduler-locking?on/step/off

          set scheduler-locking on 可以用來鎖定當(dāng)前線程,只觀察這個線程的運行情況, 當(dāng)鎖定這個線程時, 其他線程就處于了暫停狀態(tài),也就是說你在當(dāng)前線程執(zhí)行 next、step、until、finish、return 命令時,其他線程是不會運行的。

          需要注意的是,你在使用 set scheduler-locking on/step 選項時要確認(rèn)下當(dāng)前線程是否是你期望鎖定的線程,如果不是,可以使用 thread + 線程編號 切換到你需要的線程再調(diào)用 set scheduler-locking on/step 進行鎖定。

          set scheduler-locking step 也是用來鎖定當(dāng)前線程,當(dāng)且僅當(dāng)使用 next 或 step 命令做單步調(diào)試時會鎖定當(dāng)前線程,如果你使用 until、finish、return 等線程內(nèi)調(diào)試命令,但是它們不是單步命令,所以其他線程還是有機會運行的。相比較 on 選項值,step 選項值給為單步調(diào)試提供了更加精細(xì)化的控制,因為通常我們只希望在單步調(diào)試時,不希望其他線程對當(dāng)前調(diào)試的各個變量值造成影響。

          set scheduler-locking off 用于關(guān)閉鎖定當(dāng)前線程。

          我們以一個小的示例來說明這三個選項的使用吧。編寫如下代碼:

           101?#include?
          202?#include?
          303?#include?
          404?
          505?long?g?=?0;
          606?
          707?void*?worker_thread_1(void*?p)
          808?
          {
          909????while?(true)
          1010??????{
          1111??????????g?=?100;
          1212??????????printf("worker_thread_1\n");
          1313??????????usleep(300000);
          1414??????}
          1515
          1616??????return?NULL;
          1717?}
          1818
          1919?void*?worker_thread_2(void*?p)
          2020?
          {
          2121?????while?(true)
          2222??????{
          2323?????????g?=?-100;
          2424?????????printf("worker_thread_2\n");
          2525?????????usleep(500000);
          2626?????}
          2727
          2828?????return?NULL;
          2929?}
          3030
          3131?int?main()
          3232?
          {
          3333?????pthread_t?thread_id_1;
          3434?????pthread_create(&thread_id_1,?NULL,?worker_thread_1,?NULL);?
          3535?????pthread_t?thread_id_2;
          3636?????pthread_create(&thread_id_2,?NULL,?worker_thread_2,?NULL);??
          3737
          3838?????while?(true)
          3939?????{
          4040?????????g?=?-1;
          4142?????????printf("g=%d\n",?g);
          4242?????????g?=?-2;
          4343?????????printf("g=%d\n",?g);
          4444?????????g?=?-3;
          4545?????????printf("g=%d\n",?g);
          4646?????????g?=?-4;
          4747?????????printf("g=%d\n",?g);
          4848
          4949?????????usleep(1000000);
          5050??????}
          5151
          5252?????return?0;
          5353?}

          上述代碼在主線程(main 函數(shù)所在的線程)中創(chuàng)建了了兩個工作線程,主線程接下來的邏輯是在一個循環(huán)里面依次將全局變量 g 修改成 -1、-2、-3、-4,然后休眠 1 秒;工作線程 worker_thread_1、worker_thread_2 在分別在自己的循環(huán)里面將全局變量 g 修改成 100 和 -100。

          我們編譯程序后將程序使用 gdb 跑起來,三個線程同時運行,交錯輸出:

           1[root@myaliyun?xx]#?g++?-g?-o?main?main.cpp?-lpthread
          2[root@myaliyun?xx]#?gdb?main
          3...省略部分無關(guān)輸出...
          4Reading?symbols?from?main...
          5(gdb)?r
          6Starting?program:?/root/xx/main?
          7[Thread?debugging?using?libthread_db?enabled]
          8...省略部分無關(guān)輸出...
          9[New?Thread?0x7ffff6f56700?(LWP?402)]
          10worker_thread_1
          11[New?Thread?0x7ffff6755700?(LWP?403)]
          12g=-1
          13g=-2
          14g=-3
          15g=-4
          16worker_thread_2
          17worker_thread_1
          18worker_thread_2
          19worker_thread_1
          20worker_thread_1
          21g=-1
          22g=-2
          23g=-3
          24g=-4
          25worker_thread_2
          26worker_thread_1
          27worker_thread_1
          28worker_thread_2
          29worker_thread_1
          30g=-1
          31g=-2
          32g=-3
          33g=-4
          34worker_thread_2
          35worker_thread_1
          36worker_thread_1
          37worker_thread_2

          我們按 Ctrl + C 將程序中斷下來,如果當(dāng)前線程不在主線程,可以先使用 info threadsthread id切換到主線程:

           1^C
          2Thread?1?"main"?received?signal?SIGINT,?Interrupt.
          30x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          4(gdb)?info?threads
          5??Id???Target?Id???????????????????????????????Frame?
          6*?1????Thread?0x7ffff7feb740?(LWP?1191)?"main"?0x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          7??2????Thread?0x7ffff6f56700?(LWP?1195)?"main"?0x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          8??3????Thread?0x7ffff6755700?(LWP?1196)?"main"?0x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          9(gdb)?thread?1
          10[Switching?to?thread?1?(Thread?0x7ffff7feb740?(LWP?1191))]
          11#0??0x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          12(gdb)?

          然后在代碼 11 行和 41 行各加一個斷點。我們反復(fù)執(zhí)行 until 48 命令,發(fā)現(xiàn)工作線程 1 和 2 還是有機會被執(zhí)行的。

           1(gdb)?b?main.cpp:41
          2Breakpoint?1?at?0x401205:?file?main.cpp,?line?41.
          3(gdb)?b?main.cpp:11
          4Breakpoint?2?at?0x40116e:?file?main.cpp,?line?11.
          5(gdb)?until?48
          60x00007ffff704c884?in?usleep?()?from?/usr/lib64/libc.so.6
          7(gdb)?
          8worker_thread_2
          9[Switching?to?Thread?0x7ffff6f56700?(LWP?1195)]
          10
          11Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          1211??????????????????????g?=?100;
          13(gdb)?
          14worker_thread_2
          15[Switching?to?Thread?0x7ffff7feb740?(LWP?1191)]
          16
          17Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          1841??????????????????????printf("g=%d\n",?g);
          19(gdb)?
          20worker_thread_1
          21worker_thread_2
          22g=-1
          23g=-2
          24g=-3
          25g=-4
          26main?()?at?main.cpp:49
          2749??????????????????????usleep(1000000);
          28(gdb)?
          29worker_thread_2
          30[Switching?to?Thread?0x7ffff6f56700?(LWP?1195)]
          31
          32Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          3311??????????????????????g?=?100;
          34(gdb)?

          現(xiàn)在我們再次將線程切換到主線程(如果 gdb 中斷后當(dāng)前線程不是主線程的話),執(zhí)行 set scheduler-locking on 命令,然后繼續(xù)反復(fù)執(zhí)行 until 48 命令。

           1(gdb)?set?scheduler-locking?on?
          2(gdb)?until?48
          3
          4Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          541??????????????????????printf("g=%d\n",?g);
          6(gdb)?until?48
          7g=-1
          8g=-2
          9g=-3
          10g=-4
          11main?()?at?main.cpp:49
          1249??????????????????????usleep(1000000);
          13(gdb)?until?48
          14
          15Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          1641??????????????????????printf("g=%d\n",?g);
          17(gdb)?
          18g=-1
          19g=-2
          20g=-3
          21g=-4
          22main?()?at?main.cpp:49
          2349??????????????????????usleep(1000000);
          24(gdb)?until?48
          25
          26Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          2741??????????????????????printf("g=%d\n",?g);
          28(gdb)?
          29g=-1
          30g=-2
          31g=-3
          32g=-4
          33main?()?at?main.cpp:49
          3449??????????????????????usleep(1000000);
          35(gdb)?until?48
          36
          37Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          3841??????????????????????printf("g=%d\n",?g);
          39(gdb)

          我們再次使用 until 命令時,gdb 鎖定了主線程,其他兩個工作線程再也不會被執(zhí)行了,因此兩個工作線程無任何輸出。

          我們再使用 set scheduler-locking step 模式再來鎖定一下主線程,然后再次反復(fù)執(zhí)行 until 48 命令。

           1(gdb)?set?scheduler-locking?step
          2(gdb)?until?48
          3worker_thread_2
          4worker_thread_1
          5g=-100
          6g=-2
          7g=-3
          8g=-4
          9main?()?at?main.cpp:49
          1049??????????????????????usleep(1000000);
          11(gdb)?until?48
          12worker_thread_2
          13[Switching?to?Thread?0x7ffff6f56700?(LWP?1195)]
          14
          15Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          1611??????????????????????g?=?100;
          17(gdb)?until?48
          18worker_thread_2
          19worker_thread_1
          20
          21Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          2211??????????????????????g?=?100;
          23(gdb)?until?48
          24worker_thread_2
          25[Switching?to?Thread?0x7ffff7feb740?(LWP?1191)]
          26
          27Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          2841??????????????????????printf("g=%d\n",?g);
          29(gdb)?until?48
          30worker_thread_1
          31worker_thread_2
          32g=-100
          33g=-2
          34g=-3
          35g=-4
          36main?()?at?main.cpp:49
          3749??????????????????????usleep(1000000);
          38(gdb)?until?48
          39worker_thread_2
          40[Switching?to?Thread?0x7ffff6f56700?(LWP?1195)]
          41
          42Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          4311??????????????????????g?=?100;
          44(gdb)?until?48
          45worker_thread_2
          46worker_thread_1
          47
          48Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          4911??????????????????????g?=?100;
          50(gdb)

          可以看到使用 step 模式鎖定的主線程,在使用 until 命令時另外兩個工作線程仍然有執(zhí)行的機會。我們再次切換到主線程,然后使用 next 命令單步調(diào)試下試試。

           1(gdb)?info?threads
          2??Id???Target?Id???????????????????????????????Frame?
          3??1????Thread?0x7ffff7feb740?(LWP?1191)?"main"?0x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          4*?2????Thread?0x7ffff6f56700?(LWP?1195)?"main"?worker_thread_1?(p=0x0)?at?main.cpp:11
          5??3????Thread?0x7ffff6755700?(LWP?1196)?"main"?0x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          6(gdb)?thread?1
          7[Switching?to?thread?1?(Thread?0x7ffff7feb740?(LWP?1191))]
          8#0??0x00007ffff701bfad?in?nanosleep?()?from?/usr/lib64/libc.so.6
          9(gdb)?set?scheduler-locking?step
          10(gdb)?next
          11Single?stepping?until?exit?from?function?nanosleep,
          12which?has?no?line?number?information.
          130x00007ffff704c884?in?usleep?()?from?/usr/lib64/libc.so.6
          14(gdb)?next
          15Single?stepping?until?exit?from?function?usleep,
          16which?has?no?line?number?information.
          17main?()?at?main.cpp:40
          1840??????????????????????g?=?-1;
          19(gdb)?next
          20
          21Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          2241??????????????????????printf("g=%d\n",?g);
          23(gdb)?next
          24g=-1
          2542??????????????????????g?=?-2;
          26(gdb)?next
          2743??????????????????????printf("g=%d\n",?g);
          28(gdb)?next
          29g=-2
          3044??????????????????????g?=?-3;
          31(gdb)?next
          3245??????????????????????printf("g=%d\n",?g);
          33(gdb)?next
          34g=-3
          3546??????????????????????g?=?-4;
          36(gdb)?next
          3747??????????????????????printf("g=%d\n",?g);
          38(gdb)?next
          39g=-4
          4049??????????????????????usleep(1000000);
          41(gdb)?next
          4240??????????????????????g?=?-1;
          43(gdb)?next
          44
          45Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          4641??????????????????????printf("g=%d\n",?g);
          47(gdb)?next
          48g=-1
          4942??????????????????????g?=?-2;
          50(gdb)?next
          5143??????????????????????printf("g=%d\n",?g);
          52(gdb)?next
          53g=-2
          5444??????????????????????g?=?-3;
          55(gdb)?next
          5645??????????????????????printf("g=%d\n",?g);
          57(gdb)?next
          58g=-3
          5946??????????????????????g?=?-4;
          60(gdb)?next
          6147??????????????????????printf("g=%d\n",?g);
          62(gdb)?next
          63g=-4
          6449??????????????????????usleep(1000000);
          65(gdb)?next
          6640??????????????????????g?=?-1;
          67(gdb)?next
          68
          69Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          7041??????????????????????printf("g=%d\n",?g);
          71(gdb)

          此時我們發(fā)現(xiàn)設(shè)置了以 step 模式鎖定主線程,工作線程不會在單步調(diào)試主線程時被執(zhí)行,即使在工作線程設(shè)置了斷點。

          最后我們使用 set scheduler-locking off 取消對主線程的鎖定,然后繼續(xù)使用 next 命令單步調(diào)試。

           1(gdb)?set?scheduler-locking?off??
          2(gdb)?next
          3worker_thread_2
          4worker_thread_1
          5g=-100
          642??????????????????????g?=?-2;
          7(gdb)?next
          8worker_thread_2
          9[Switching?to?Thread?0x7ffff6f56700?(LWP?1195)]
          10
          11Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          1211??????????????????????g?=?100;
          13(gdb)?next
          14g=100
          15g=-3
          16g=-4
          17worker_thread_2
          1812??????????????????????printf("worker_thread_1\n");
          19(gdb)?next
          20worker_thread_1
          2113??????????????????????usleep(300000);
          22(gdb)?next
          23worker_thread_2
          24[Switching?to?Thread?0x7ffff7feb740?(LWP?1191)]
          25
          26Thread?1?"main"?hit?Breakpoint?1,?main?()?at?main.cpp:41
          2741??????????????????????printf("g=%d\n",?g);
          28(gdb)?next
          29[Switching?to?Thread?0x7ffff6f56700?(LWP?1195)]
          30
          31Thread?2?"main"?hit?Breakpoint?2,?worker_thread_1?(p=0x0)?at?main.cpp:11
          3211??????????????????????g?=?100;
          33(gdb)?next
          34g=-1
          35g=-2
          36g=-3
          37g=-4
          38worker_thread_2
          3912??????????????????????printf("worker_thread_1\n");
          40(gdb)?

          取消了鎖定之后,單步調(diào)試時三個線程都有機會被執(zhí)行,線程 1 的斷點也會被正常觸發(fā)。

          至此,我們搞清楚了如何利用 set scheduler-locking 選項來方便我們調(diào)試多線程程序。

          總而言之,熟練掌握 gdb 調(diào)試等于擁有了學(xué)習(xí)優(yōu)秀 C/C++ 開源項目源碼的鑰匙,只要可以利用 gdb 調(diào)試,再復(fù)雜的項目,在不斷調(diào)試和分析過程中總會有搞明白的一天。

          —?【 THE END 】—本公眾號全部博文已整理成一個目錄,請在公眾號里回復(fù)「m」獲??!


          3T技術(shù)資源大放送!包括但不限于:Java、C/C++,Linux,Python,大數(shù)據(jù),人工智能等等。在公眾號內(nèi)回復(fù)「1024」,即可免費獲?。。?/span>




          瀏覽 80
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日噜| 亚洲综合色吧 | 青青草成人在线观看 | 18禁黄网站网址免费入口 | 婷婷综合网站 |