記錄乙個單執行緒呼叫寫出現的 死鎖 問題

2021-08-17 17:32:13 字數 2878 閱讀 9308

問題的現象是出現乙個pthread_mutex_t 互斥鎖在init成功後, 在 pthread_mutex_lock 的時候出現死鎖, 然後程式卡住一直執行不下去, 但是呼叫的地方只有在乙個執行緒中, 沒有多執行緒同時呼叫pthread_mutex_lock的情況 ;

這種情況下, 調查一般死鎖的思路是完全沒用的,而且會懷疑人生,所以必須要像寒戰裡劉傑輝破案一樣, 要更巨集觀的去找原因,o(∩_∩)o哈哈~; 發現這個問題時,當時用的是列印 pthread_mutex_t 的 __data.__ownwer成員發現是乙個很大的數,完全不像乙個執行緒id;

這種"神奇"的bug現象下,rootcause完全跟執行緒,互斥所沒有一點毛的關係, 而真正的原因是呼叫程式和被呼叫程式使用的標頭檔案不一致而造成的記憶體破壞, 更確切的舉例說, 比如, 被呼叫方的的乙個c++類的的定義改變了,增加,刪除或修改了幾個成員, 但是成員函式介面都沒有變, 而只是資料成員變了,並且成員函式內部實現變了; 這時候,如果呼叫這個類的**跟這個類的實現不在乙個庫中, 也就是分別編譯的, 那麼如果只更新編譯了被呼叫方, 而不使用新的標頭檔案重新編譯呼叫方的話, 整個程式執行就可能有問題; 

因為在這個例子中,呼叫方直接使用被呼叫的那個類的物件變數作為自己類的成員變數, 而不是使用的被呼叫類的指標作為自己類的資料成員, 在這個例子中,就看出了指標使用的好處; 直接使用類物件的情況下,相當於自己類的記憶體布局中包含了呼叫類的記憶體, 而當上述標頭檔案不一致的情況發生時, 在執行時,自己的類記憶體的布局還是原來舊的布局, 而被呼叫類的庫已經更新了,這時候被呼叫類的庫的執行**會按新的記憶體布局來訪問現在的呼叫類的記憶體中的被調類記憶體,顯然會寫出錯,於是造成各種很奇怪的問題,最終原因都是標頭檔案不一致導致的記憶體破壞;這種問題經常發現在系統整合的測試中,一般單獨的測試不太容易測到; 乙個號稱自己擅長系統整合的人或公司,應該對這種問題特別了解;

那麼從程式設計和系統整合的角度擴充套件開來時, 兩個類存在包含關係時, 最好不要用類物件直接作為成員變數, 做好用指標+inte***ce的方式, 可以配合上只能指標等形式, 這樣, 能夠保證呼叫類的記憶體不會被被呼叫類的記憶體而影響; 因為被呼叫類的記憶體的分配就和此時呼叫類的構造和初始化分配是分開的; 從而這種整合是更鬆耦合的; 從原理上,指標這種型別的使用, 也是從系統角度去管理記憶體,因為指標實際指向的記憶體情況由系統來負責, 把更多的事情交給能夠在第一時間發現任何元件變化的系統來做, 才是更符合系統整合的思路, 才能更容易發現和解決問題,而且也才是更魯棒的系統整合方式;

智慧型指標使用的一些總結;

unique_ptr不同,標準庫並不提供shared_ptr,因此,使用shared_ptr處理陣列時需要顯示指定刪除行為,例如:

1

2

3

4

5

6

shared_ptr

<

string> ptr1(

newstring[

10],

( string *p ) );

shared_ptr

<

string> ptr2(

newstring[

10],

std::default_delete<

string>() );

最好不要直接使用智慧型指標訪問陣列資料。可行得辦法是使用智慧型指標管理陣列記憶體,但是使用原始指標訪問陣列資料;

std::allocate_shared的例子看;
std::

default_delete(模板函式物件類)

function object class (過載了()操作符的類)

// allocate_shared example

#include #include class tree

tree(int b, int c)

public:

void print()

private:

int _a;

int _b;

int _c;

};int main ()

class x

// function object class example

#include #include #include #include class funcobjclass

};int main(int argc, char *argv)

//std::for_each(vec.begin(), vec.end(), funcobjclass());

std::for_each(vec.begin(), vec.end(), fn);

return 0;

}

//delete example

#include using namespace std;

class t

~t()

};int main()

在delete example中,如果使用了delete p1, 則會引起後面程式的core dump,這種錯誤報錯的位置已經離開了真正錯誤**的位置, 特別需要避免;

另外, 像 這樣的c++11-style 

lambda expression

("lambda function"). it takes a pointer to an integer anddeletes it.

(int* p)
需要了解

lambda expression

乙個簡單執行緒類的實現

很多時候,在c 類中,我們需要建立乙個輔助線程,用來處理後台的資料,每當敲起 來的時候卻發現有很多顧慮,執行緒過程函式必須為全域性或者靜態函式,而在靜態函式類部又不能呼叫本類的非靜態成員函式,總感覺每一步得小心謹慎,像黑夜的時候光腳趕路一樣,想走快卻又怕被石子絆住。下面實現乙個簡單執行緒類,方便呼叫...

iOS pthread t自定義乙個簡單執行緒

有時候,會使用更原始的執行緒程式設計!如下示例 qbkthread.h inte ce qbkthread nsobject void run void start void stop end qbkthread.m import qbkthread.h import void background...

乙個簡單執行緒池的實現 C語言

本文的 實現了乙個簡單的執行緒池,並利用c s模型將客戶端所傳送的資料進行簡單的計算。我是這麼理解的,池是電腦科學中一種典型的機制,如stl裡面的記憶體池,資料庫連線池,還有程序池或執行緒池。這些池建立的目的都是為了避免頻繁系統呼叫所帶來的開銷。我們這樣想,如果我們事先轉備好一大塊所需要的記憶體,或...