<p id="m2nkj"><option id="m2nkj"><big id="m2nkj"></big></option></p>
    <strong id="m2nkj"></strong>
    <ruby id="m2nkj"></ruby>

    <var id="m2nkj"></var>
  • 如何調(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
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

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

    手機掃一掃分享

    分享
    舉報
    <p id="m2nkj"><option id="m2nkj"><big id="m2nkj"></big></option></p>
    <strong id="m2nkj"></strong>
    <ruby id="m2nkj"></ruby>

    <var id="m2nkj"></var>
  • 国产成人综合欧美精品久久 | www.操嫩逼 | 黄色福利视频 | 国语对白在线视频 | 九色视频在线观看 | 正在播放蜜臀av 正在播放做爱内射 | 亚洲人做受高 | 蜜乳网| 在线观看的A片 | 逼特逼网站在线观看 |