多線程死鎖調試小技巧

  聽說再高的高手在寫多線程程序的時候都難確保不會產生死鎖,死鎖的調試也就成爲一個比較常見的問題,假設有下面這樣一個問題:ios

  一個正在生產環境下運行的進程死鎖了,或者你只是在跑一個程序,並無在調試器裏面打開它,而後發現沒有響應,日誌輸出也中止了。因爲你是一個有經驗的程序員,會想到「我剛剛加上了新的鎖策略,不必定穩定,這多是死鎖了「。可是你不想就這麼殺掉進程,由於多線程的 bug 不容易重現,趕上一次死鎖可能要憑運氣,錯過了此次,它下次死鎖可能會出如今你演示給老闆看的時候……怎麼辦?c++

  對於這樣的問題能夠藉助Core Dump來調試。程序員

  什麼是Core Dump?ubuntu

  Core的意思是內存, Dump的意思是扔出來, 堆出來.開發和使用Unix程序時, 有時程序莫名其妙的down了, 卻沒有任何的提示(有時候會提示core dumped). 這時候能夠查看一下有沒有形如core.進程號的文件生成運行過程當中發生異常, 程序異常退出時, 由操做系統把程序當前的內存情況存儲在一個core文件中, 叫core dump.這個文件即是操做系統把程序down掉時的內存內容扔出來生成的, 它能夠作爲調試程序的參考.多線程

  Core Dump又叫核心轉儲, 當程序沒有core文件生成怎麼辦呢?app

  有時候程序down了, 可是core文件卻沒有生成,core文件的生成跟你當前系統的環境設置有關係, 能夠用下面的語句設置一下, 而後再運行程序便會生成core文件.this

  ulimit -c unlimitedspa

  core文件生成的位置通常於運行程序的路徑相同, 文件名通常爲core.進程號,在個人ubuntu12.04lts下生產的文件名爲core。操作系統

  介紹了core dump以後,來看看如何在多線程調試中使用core dump。線程

  使用 kill 命令產生 core dump文件:

  kill -11 pid

  這不仍是殺掉進程嘛?沒錯,可是你用信號11殺掉它,會讓進程產生一個 Segmentation Fault,從而(若是你沒禁用 core dump 的話),致使一個 core dump。隨後你獲得一個 core 文件,裏面包含了死鎖的時候,進程的內存鏡像,也就包括了正在糾結纏綿,生離死別從而產生死鎖的那兩個,沒準是幾個,線程們的,棧。

  如今知道該怎麼辦了吧?用 gdb 打開這個 core 文件,而後

  thread apply all bt

  gdb 會打出全部線程的棧,若是你發現有那麼幾個棧停在 pthread_wait 或者相似調用上,大體就能夠得出結論:就是它們幾個兒女情長,耽誤了整個進程。

  下面我來舉一個簡單的例子(爲了代碼儘可能簡單,使用了C++11的thread library)

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;

mutex m1,m2;


void func_2()
{
    m2.lock();
    cout<< "about to dead_lock"<<endl;
    m1.lock();
    
}

void func_1()
{
    m1.lock();
    
    chrono::milliseconds dura( 1000 );// delay to trigger dead_lock
    this_thread::sleep_for( dura );
        
    m2.lock();
    
}


int main()
{

    thread t1(func_1);

    thread t2(func_2);
    
    t1.join();
    t2.join();
    return 0;

}

 

  編譯代碼

  $> g++ -Wall -std=c++11 dead_lock_demo.cpp -o dead_lock_demo -g -pthread

  運行程序,發現程序打印出「about to dead_lock」 就不動了,如今咱們使用gdb來調試。注意gdb的版本要高於7.0,以前使用過gdb6.3調試多線程是不行的。

  在這以前須要先產生core dump文件:

  $> ps -aux | grep dead_lock_demo

  找出 dead_lock_demo 線程號,而後:

  $> kill -11 pid

  此時會生成core dump 文件,在個人系統上名字就是 core

  而後調試:

  $> gdb dead_lock_demo core

  $> thread apply all bt

  下面來看一下實際的過程:

  

  從上圖能夠看出兩個線程都阻塞在wait上,並且還給出了在哪一行代碼中,很容易就定位到產生死鎖的位置。

相關文章
相關標籤/搜索