std :: список потоков push_back, front, pop_front - PullRequest
6 голосов
/ 04 декабря 2009

Безопасен ли поток std :: list? Я предполагаю, что это не так, я добавил свои собственные механизмы синхронизации (я думаю, что у меня есть правильный термин). Но я все еще сталкиваюсь с проблемами

Каждая функция вызывается отдельным потоком. 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();
    }
}

После примерно 170 тыс. Звонков в thread1 и 900 тыс. Звонков в thread2 я получаю сообщение об ошибке исключения CFoo& pFoo = g_buffer.front() ;

Это вызывает сбой программы. stdthrow.cpp: 22 * ​​1009 *

#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 ]

15 голосов
/ 04 декабря 2009

Безопасен ли поток std :: list?

Текущий стандарт C ++ даже не признает существование потоков, поэтому std::list определенно нет. Однако разные реализации могут обеспечить (разные уровни) безопасности потоков.

Что касается вашего кода: если вам нужна блокировка, используйте блокировку. Эта переменная bool может не помочь, когда потоки выполняются на разных ядрах, которые извлекают ее из разных кешей. Вместо этого используйте настоящий мьютекс.

6 голосов
/ 04 декабря 2009

Нет, потокобезопасность не гарантируется.

Ваш механизм синхронизации имеет недостатки. Вы разрешаете thread1 изменять список, пока thread2 работает с ним. Это может вызвать проблемы. Кроме того, вы должны сделать вашу блокировку переменной volatile.

1 голос
/ 04 декабря 2009

То, что Thread1 должен быть максимально быстрым, не означает, что можно разрешить ему доступ к списку во время его изменения другим потоком. Поскольку оба потока изменяют список, оба должны ждать. Быстрая скорость не поможет, если вы в конечном итоге получите поврежденные данные.

Edit:

На самом деле вам это может сойти с рук ... Thread1 только добавляет элементы в список, а Thread2 только удаляет их. Это означает, что Thread1 нужно только подождать, если список содержит только один элемент.

Edit2:

Таким образом, способ сделать эту работу состоит в том, чтобы thread2 блокировал список, если он содержит только один элемент. Это должно было бы проверить это перед каждым удалением. Таким образом, потоку 1 не придется ждать, за исключением одного этого случая.

И вам определенно следует использовать правильный механизм взаимного исключения (все, что доступно на вашей платформе), а не логический флаг.

1 голос
/ 04 декабря 2009

Вы правы, предполагая, что список stl не гарантированно является поточно-ориентированным.

Также ваш механизм синхронизации, вероятно, не очень хорош.

Внутри резьбы2 вы не блокируете ваш бул, поэтому у вас там проблема.

Скорее всего, как минимум, вы должны поставить квалификатор volatile перед блокирующим логическим элементом; или, что еще лучше, посмотрите реальные функции мьютекса, соответствующие вашей платформе.

0 голосов
/ 04 декабря 2009

Если вам действительно нужен поток 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, так что это сохранит некоторую конкуренцию за блокировку, которая в противном случае возникла бы при использовании только одного списка буферов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...