2009-12-03 24 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运行的次数多,所以这将节省一些锁争用,否则会发生只使用一个缓冲区列表。

相关问题