2009-12-03 52 views
5

是std :: list線程安全嗎?我假設它不是,所以我添加了我自己的同步機制(我認爲我有合適的期限)。但我仍然遇到問題std :: list線程push_back,前面,pop_front

每個函數都由一個單獨的線程調用。線程1等不及了,它必須儘可能快地

std::list<CFoo> g_buffer; 
bool g_buffer_lock; 

void thread1(CFoo frame) { 
    g_buffer_lock = true ; 
    g_buffer.push_back(frame) ; 
    g_buffer_lock = false; 
} 


void thread2() 
{ 
    while(g_buffer_lock) { 
     // Wait 
    } 

    // CMSTP_Send_Frame * pMSTPFrame = NULL ; 
    while (! g_buffer_lock && g_buffer.size() > 0) 
    { 
     // Get the top item 
     CFoo& pFoo = g_buffer.front() ; 

     // Do something. 

     // remove the front item 
     g_buffer.pop_front(); 
    } 
} 

經過約170,000調用線程1和900K調用線程2我對CFoo& pFoo = g_buffer.front() ;

這會導致程序崩潰得到一個異常錯誤。 stdthrow.cpp:22

#ifdef _DEBUG 
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Debug_message(const wchar_t *message, const wchar_t *file, unsigned int line) 
    { // report error and die 
     if(::_CrtDbgReportW(_CRT_ASSERT, file, line, NULL, message)==1) 
     { 
      ::_CrtDbgBreak(); 
     } 
    } 
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Debug_message(const unsigned short *message, const unsigned short *file, unsigned int line) 
    { // report error and die 
     _Debug_message((wchar_t *) message, (wchar_t *) file, line); 
    } 

#endif 

建議,意見,是否有更好的做事方式?

+4

是std :: list線程安全嗎? No. – KeatsPeeks 2009-12-03 22:48:57

回答

15

是std :: list線程安全嗎?

當前的C++標準甚至不承認線程的存在,所以std::list當然不是。不過,不同的實現可能會提供(不同級別的)線程安全性。

至於你的代碼:如果你需要一個鎖,使用一個鎖。當線程在不同的核心上執行時,bool變量可能無法提供幫助。改用真正的互斥體。

6

不,它不保證是線程安全的。

您的同步機制存在缺陷。您允許thread1更改列表,而thread2正在使用它。這可能會導致問題。除此之外,你應該讓你的鎖變量volatile

+1

添加了等待兩項功能的檢查。運行測試以查看它是否可以解決此問題。 我會想,因爲thread1只添加項目,thread2只刪除不會導致任何問題的項目。但它看起來像它。 – 2009-12-03 23:10:58

+3

@Steven:正如sbi在他的回答中指出的那樣,您最好使用系統提供的鎖定機制,而不是滾動自己的鎖定機制。 – 2009-12-03 23:12:58

1

你是正確的假設stl列表不保證是線程安全的。

同樣你的同步機制不太可能。

內線2你不要你的布爾,所以你有一個問題。

你應該至少把一個不穩定的限定符放在你的鎖布爾的前面;或者更好地研究適合您的平臺的真正的互斥函數。

+6

易失性不是內存屏障的替代品,並且在需要真正的內存屏障時不起作用。 – 2009-12-03 22:54:56

1

只是因爲Thread1需要儘可能快,它並不意味着它可以訪問一個列表,而它正在被另一個線程所改變是可以的。由於兩個線程都在修改列表,因此都必須等待。如果你最終得到了損壞的數據,那麼速度並不起作用。

編輯:

其實你可以逃脫它...線程1只添加元素到列表中,而線程2只刪除它們。這意味着如果列表只包含一個元素,Thread1只需要等待。

EDIT2:

因此,爲了使這項工作的方法是線程2鎖定列表,如果它僅包含一個元素。它必須在每次刪除之前檢查。除此之外,thread1不需要等待。

而且您絕對應該使用適當的互斥機制(無論在您的平臺上可用)而不是布爾標誌。

+1

這也是我的想法,因爲thread1添加和thread2刪除項目,我可以在同一時間運行它們......但在實踐中似乎並非如此。 – 2009-12-03 23:08:33

+0

這是因爲你不知道thread1試圖添加時thread2刪除了多少個元素... – Dima 2009-12-03 23:10:30

+1

我想我明白了。如果thread2將要刪除最後一個元素,它應該只鎖定列表。除此之外,thread1永遠不需要等待。 – Dima 2009-12-03 23:12:27

0

如果你真的需要線程1是儘可能地快,但仍需要線程安全,可以防止在開銷最少量的費用的一些鎖爭用,因爲這樣的:

std::list<CFoo> g_buffer_thread1; 
std::list<CFoo> g_buffer_thread2; 
Mutex g_mutex; 

void thread1(CFoo frame) { 
    Locker lock(g_mutex); 
    g_buffer_thread1.push_back(frame) ; 
} 


void thread2() 
{ 
    while(g_buffer_thread2.size() == 0) { 
     // Wait? 
     Locker lock(g_mutex); 
     g_buffer_thread1.swap(g_buffer_thread2); 
    } 

    while (g_buffer_thread2.size() > 0) 
    { 
     CFoo& pFoo = g_buffer_thread2.front() ; 
     // Do something. 
     g_buffer_thread2.pop_front(); 
    } 
} 

我想這是線程安全性最直接的組合。不幸的是,Thread1必須始終鎖定。你可能會想出一些你爲thread1批幀的東西。我假設,根據你在問題中的數字,thread1比thread2運行的次數多,所以這將節省一些鎖爭用,否則會發生只使用一個緩衝區列表。

相關問題