日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

多線程編程是現代軟件開發中的一項重要技術,但隨之而來的挑戰之一是多線程死鎖。多線程死鎖是程序中的一種常見問題,它會導致線程相互等待,陷入無法繼續執行的狀態。這里,我們將探討多線程死鎖的概念、原理,同時我們通過一個例子來介紹如何使用GDB(GNU Debugger)這一工具來排查和解決多線程死鎖問題。

解鎖多線程死鎖之謎:深入探討使用GDB調試的技巧

多線程死鎖的概念

 

多線程死鎖是多線程編程中的一種關鍵問題。它發生在多個線程試圖獲取一組資源(通常是鎖或資源對象)時,導致彼此相互等待的情況。具體來說,當線程1持有資源A并等待資源B,而線程2持有資源B并等待資源A時,就可能發生死鎖。

多線程死鎖原理

 

為了更好地理解多線程死鎖的原理,讓我們考慮一個簡單的示例。假設有兩個資源A和B,以及兩個線程(Thread 1和Thread 2)。線程1需要獲取資源A和B,線程2需要獲取資源B和A。如果線程1獲取了資源A,而線程2獲取了資源B,它們都無法繼續,因為它們都需要對方持有的資源才能繼續。這就是典型的死鎖情況。

 

多線程死鎖通常發生在以下情況下:

  • 線程同時持有一個資源并等待另一個資源。
  • 資源分配不當,線程沒有按照相同的順序獲取資源。

多線程死鎖之所以會發生,是因為線程之間的相互依賴和等待。當多個線程需要共享資源時,它們可能會按不同的順序獲取這些資源,導致資源互斥問題,最終引發死鎖。

排查多線程死鎖

GDB是一個強大的調試工具,可以用來排查多線程死鎖問題。下面通過一個例子來說下如何使用gdb調試死鎖問題,這也是前段時間我碰鎖問題新學到的技能。

簡單的代碼如下:


#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t exit_condition = PTHREAD_COND_INITIALIZER;
int should_exit = 0;

void *thread1_function(void *arg) {
    while (1) {
        printf("Thread 1: Attempting to acquire mutex1...n");
        pthread_mutex_lock(&mutex1);
        printf("Thread 1: Acquired mutex1.n");

        printf("Thread 1: Attempting to acquire mutex2...n");
        pthread_mutex_lock(&mutex2);
        printf("Thread 1: Acquired mutex2.n");

        // 在此處檢查是否應該退出
        if (should_exit) {
            pthread_mutex_unlock(&mutex2);
            pthread_mutex_unlock(&mutex1);
      break;
        }

        pthread_mutex_unlock(&mutex1);
        pthread_mutex_unlock(&mutex2);
    }
    printf("Thread 1 exit done!n");
    pthread_exit(NULL);
}

void *thread2_function(void *arg) {
    sleep(5); // 讓線程2休眠10秒鐘

    printf("Thread 2: Attempting to acquire mutex2...n");
    pthread_mutex_lock(&mutex2);
    printf("Thread 2: Acquired mutex2.n");

    printf("Thread 2: Notifying Thread 1 to exit...n");
    should_exit = 1;
    pthread_cond_signal(&exit_condition);

    //通過不釋放該鎖制造死鎖
    pthread_mutex_unlock(&mutex2);

    printf("Thread 2 exit done!n");
    //exit執行后不會再執行該函數后面部分
    pthread_exit(NULL);
}

int mAIn() {
    pthread_t thread1, thread2;

    pthread_create(&thread1, NULL, thread1_function, NULL);
    pthread_create(&thread2, NULL, thread2_function, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

代碼很簡單,通過創建兩個線程,線程1睡眠5s為mutex2加鎖并通知線程1進行退出,之后線程2退出,線程1是個while循環,不停的對mutex1進行加解鎖,并加鎖后檢測是否退出,退出則對mutex2進行加鎖打印,然后釋放mutex1、mutex2進行退出。

使用:gcc thread.c -g -lpthread -o thread編譯,因為要gdb調試所以需要帶上-g參數,正常現象會執行結束打印如下:

解鎖多線程死鎖之謎:深入探討使用GDB調試的技巧

現在我們屏蔽掉線程2釋放mutex2進行死鎖調試:

void *thread2_function(void *arg) {
    sleep(5); // 讓線程2休眠10秒鐘

    printf("Thread 2: Attempting to acquire mutex2...n");
    pthread_mutex_lock(&mutex2);
    printf("Thread 2: Acquired mutex2.n");

    printf("Thread 2: Notifying Thread 1 to exit...n");
    should_exit = 1;
    pthread_cond_signal(&exit_condition);

    //通過不釋放該鎖制造死鎖
    //pthread_mutex_unlock(&mutex2);

    printf("Thread 2 exit done!n");
    //exit執行后不會再執行該函數后面部分
    pthread_exit(NULL);
}

實際環境中我們并不知道死鎖發生,所以我們通過gdb先運行一次直到程序無法正常退出時,執行bt查看堆棧:

解鎖多線程死鎖之謎:深入探討使用GDB調試的技巧

這里因為加了打印所以很快可以看到mutex2上鎖那里卡住,實際環境會有很多線程運行,我們并不直到哪里會有問題,此時只能通過bt查看堆棧我們發現卡在函數__futex_abstimed_wait_common64,運行到./nptl/futex-internal.c文件第57行。

這里我們只需要知道該函數__futex_abstimed_wait_common64是linux內核中用于處理互斥鎖等待超時的一個內部函數即可。

此時可以斷定代碼存在死鎖問題了,我們繼續排查。

我們繼續看bt信息,發現該等待是從#4  0x00005555555553c8 in main () at thread.c:59調入的,因為前面是#4,所以使用f 4進入該函數。

解鎖多線程死鎖之謎:深入探討使用GDB調試的技巧

 

我們發現是main里調入,同時在執行thread1的pthread_join,所以前面的__futex_abstimed_wait_common64并不是我們真正要找的問題,其實thread1已經來到了join的位置,等待結束了。我們繼續執行thread Apply all bt把所有線程堆棧打出來看下:

解鎖多線程死鎖之謎:深入探討使用GDB調試的技巧

根據前面分析thread 1已經正常退出了,我們這里看到thread 2卡在futex_wait,根據上下文非常明顯是在等待futex lock,再往下看我們發現鎖mutex2,這里就是thread2在等待mutex2,那么mutex2被誰lock住沒釋放呢?我們通過p mutex2來查看owner即可知道該鎖被誰擁有。

解鎖多線程死鎖之謎:深入探討使用GDB調試的技巧

這里有個問題,是因為該代碼恰巧thread 1退出等待join了,所以這里的23890是個內核線程,在持有著mutex2,實際環境中我們會看到owner大概會是info threads中的LWP,于是就可以定位到該鎖被誰持有沒有釋放了,再分析代碼即可。

我把thread 1再改下,不直接退出而是一直while(1)的形態來測試,此時再通過上述來查找mutex2被誰持有即可直觀看到:

解鎖多線程死鎖之謎:深入探討使用GDB調試的技巧

分享到:
標簽:死鎖
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定