std :: list threading push_back,front,pop_front

时间:2009-12-03 22:46:39

标签: c++ stl multithreading

std :: list thread是否安全?我假设它不是这样我添加了自己的同步机制(我认为我有正确的术语)。但我仍然遇到问题

每个函数都由一个单独的线程调用。 Thread1不能等待,它必须尽可能快

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();
    }
}

在大约170k调用thread1和900k调用thread2后,我在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

建议,意见,是否有更好的做事方式?

5 个答案:

答案 0 :(得分:15)

  

std :: list thread是否安全?

当前的C ++标准甚至不承认线程的存在,因此std::list当然不是。然而,不同的实现可能提供(不同级别的)线程安全性。

至于你的代码:如果你需要锁,请使用锁。当线程在不同的核心上执行时,bool变量可能无效,这些核心从不同的缓存中获取它。请改用真正的互斥锁。

答案 1 :(得分:6)

不,它不能保证是线程安全的。

您的同步机制存在缺陷。 thread1正在使用thread2时,您允许volatile更改列表。这可能会导致问题。除此之外,你应该使你的锁变量{{1}}。

答案 2 :(得分:1)

你认为stl列表不能保证是线程安全的,这是正确的。

您的同步机制也不太可能。

在thread2中,你没有锁定你的bool,所以你遇到了问题。

您应该至少在锁定布尔之前放置一个易变限定符;或者更好地了解适合您平台的真实互斥功能。

答案 3 :(得分:1)

仅仅因为Thread1需要尽可能快,这并不意味着让它在被另一个线程更改时访问列表是可以的。由于两个线程都在修改列表,因此两者都必须等待。如果你最终得到了腐败的数据,那么快速无济于事。

编辑:

实际上你可能会侥幸逃脱...... Thread1只会向列表中添加元素,而Thread2只会删除它们。这意味着如果列表只包含一个元素,则Thread1只需要等待。

EDIT2:

因此,使这项工作的方法是让thread2锁定列表,如果它只包含一个元素。它必须在每次删除之前检查。这样,除了那个案例之外,thread1不必等待。

你绝对应该使用适当的互斥机制(平台上可用的任何东西)而不是布尔标志。

答案 4 :(得分:0)

如果你真的需要thread1尽可能快,但仍然需要线程安全,你可以以最小的开销为代价来防止一些锁争用,如下:

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