[C++] STL map 在多執行緒下新增/刪除,造成 crash 的問題

[C++] STL map 在多執行緒下新增/刪除,造成 crash 的問題

最近在查一個 C++ 程式莫名其妙會當掉的問題…

蠻難查的,因為通常是跑了幾個小時後才會出現,

但如果拿 core dump 出現當時的資料重新跑一次,就好好的什麼事都沒發生…

core dump 裡面也常常是看不出什麼東西,一堆問號,載入 symbol 也沒什麼資訊…

 

開始做 code review 之後,首先懷疑是一個 STL vector 被設定成 class 的 static 變數,

其實就跟全域變數差不多了,但又有多個 thread 可以去修改/讀取,感覺就很可疑…

std::vector<int> Scanner::m_vecIdx;
void Scanner::Add(int i)
{
if (std::find(m_vecIdx.begin(), m_vecIdx.end(), i) == m_vecIdx.end())
{
m_vecIdx.push_back(i);
}
}
void Scanner::Clear()
{
m_vecIdx.clear();
}

 

姑且先不論是否是這邊造成 crash,但不同 thread 理論上要處理不同的資料,

用全域變數共用資料顯然有問題,一定得修改…

因為程式裡有其他部分有用 thread id 來區分不同 thread 的資料,

所以先照原程式的寫法,把 vector 換成 map<pthread_t, vector>,

也就是每個 thread 有自己的 vector,才不會互相影響~

std::map<pthread_t, std::vector<int> > Scanner::m_mapIdx;

 

這樣改完之後,當掉的次數變少了,不過還是會當?!

又看了幾次程式,開始在想 multi-thread 在修改/讀取 map 時,會不會有問題呢?

看了幾篇文章,結論是:

  – 對 map 來說,資料的新增/刪除不影響現有的 iterator

  – 如果有資料的新增/修改的話,不能同時有其他 thread 對同一個 container 的存取

 

以上面的程式來說,雖然不同的 thread 會去使用 map 的不同部分,

例如 thread 1 用 m_mapIdx[1] 的 vector,thread 2 用 m_mapIdx[2] 的 vector,

但對 m_mapIdx 做新增/刪除的動作並不是 thread-safe 的…

假設突然有新的 thread 3 和 4 出現來呼叫 Add 函式,

程式寫法會導致 m_mapIdx 必須要新增給 thread 3 和 4 的資料,

但這動作不是 atomic 的,有可能在新增給 thread 3 的動作做一半時,

被切到 thread 4 去新增給 thread 4 的資料,導致 map 爛掉…

 

目前的改法是在使用到這個 map 的地方,都先用一個 mutex 保護住,

避免有兩個以上的 thread 同時去修改或讀取…

CAutoMutex autoMutex(g_mutex);
pthread_t tid = pthread_self();
std::vector<int>& vecIdx = m_mapIdx[tid];
vecIdx.push_back(i);

 

當然上面這並不是最好的做法,理論上同時多根 thread 的讀取應該沒有問題,

但這得建立在 map 不會突然有新的 thread 進來要新增的前提之下…

在程式原作者放假回來之前,就只好先這樣頂著用了~~

 

參考資料:

stackoverflow: Iterator invalidation rules

stackoverflow: Thread-safety of reading and writing from/to std::map in C++

stackoverflow: What operations are thread-safe on std::map?

 

(本頁面已被瀏覽過 708 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料