最近在做項目的過程中,遇到STL中vector的多線程訪問問題。問題大概是這樣的:有一個全局的vector,一個寫進程對該vector進行插入操作(push_back()),同時有一個讀進程在監視該vector的內容並對其進行顯示(操作:size(), at(i)),沒有進行任何的線程同步,程序的編譯沒有任何問題,卻一直出現運行時錯誤,主要是數組越界。 當時的考慮時:雖然vector不支持多線程,但是我的兩個線程,一個寫,一個卻是只讀,按理說不應該有問題,大不了有臟數據,但是我不在乎。網上有很多人說是線程同步問題,但是個人感覺不是這個問題。后來看了一篇網文之后才晃然大悟,其實是vector的大小動態分配所造成的問題。簡單的說就是讀進程和寫進程在vector的內存動態重分配時內存地址不同步了(不知道說的對不對)。懶的再描述自己的具體問題了,直接把那篇網文貼上來,以供以后查看。
原文地址:http://donghao.org/2008/10/vectorouaissiiaaieia.html
原文內容:
程序出現了coredump,用gdb發現了出錯的地方。
vector<size_t> seq_offset;
......
if(seq_id < seq_offset.size())
{
seq_offset[seq_id]; //此處coredump
}
沒有超過vector的大小,訪問是不應該有問題的,而且這一現象是偶然出現,所以懷疑是多線程的原因。果然在另一個線程里發現對seq_offset有 insert操作,insert本身不會影響訪問,但是當vector大小不夠的時候,insert會引發內存重新分配。STL代碼(linux gcc-3.4.3 /usr/include/c++/3.4.3/bits)里,vector的insert會調用_M_fill_insert,而當空間不夠時 _M_fill_insert會重新分配空間,並將相關數據結構指向新空間。
文件 vector.tcc
324 std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish); //銷毀vector里的元素,即調用各元素的析構函數
325 _M_deallocate(this->_M_impl._M_start, //收回存放各元素的空間,即free
326 this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
327 this->_M_impl._M_start = __new_start.base(); //數據結構指向新的空間
328 this->_M_impl._M_finish = __new_finish.base();
329 this->_M_impl._M_end_of_storage = __new_start.base() + __len;
麻煩就在於當326行結束后,_M_start指向的是一塊已經釋放了的空間,這時候再有vector的訪問操作(如 seq_offset[seq_id])當然就會coredump。其實STL完全可以先把_M_start賦給一個臨時變量,然后_M_start指向 新空間,最后再釋放臨時變量指向的空間,這樣可以保證在多線程下順利運行。不過STL文檔說了——它不支持多線程——所以這樣做也沒錯。
加鎖也可以解決這個問題,不過那樣太低效了,不予考慮。最后的解決方案是,用vector的reserve方法預先分配好內存,免得在使用中動態增長。