Почему эта потокобезопасная очередь создает тупик? - PullRequest
0 голосов
/ 24 августа 2010

Я написал свою собственную версию потоковой очереди.Тем не менее, когда я запускаю эту программу, она сама зависает / блокируется.

Интересно, почему она блокируется / зависает навсегда.

void concurrentqueue::addtoQueue(const int number)
{
    locker currentlock(lock_for_queue);
    numberlist.push(number);
    pthread_cond_signal(&queue_availability_condition);
}

int concurrentqueue::getFromQueue()
{
    int number = 0;


    locker currentlock(lock_for_queue);
    if ( empty() )
    {
        pthread_cond_wait(&queue_availability_condition,&lock_for_queue);
    }

    number = numberlist.front();
    numberlist.pop();
    return number;
}

bool concurrentqueue::empty()
{       
    return numberlist.empty();
}

Я написал, как блокировка классов как RAII.

class locker
{
public:
    locker(pthread_mutex_t& lockee): target(lockee)
    {
        pthread_mutex_lock(&target);
    }
    ~locker()
    {
        pthread_mutex_unlock(&target);
    }
private:
        pthread_mutex_t target;
};

Мой код потока писателя / читателя очень прост.Запись потока, добавляет в очередь и поток чтения, читает из очереди.

void * writeintoqueue(void* myqueue)
{
    void *t = 0;
    concurrentqueue *localqueue = (concurrentqueue *) myqueue;

    for ( int i = 0; i < 10 ; ++i)
    {
        localqueue->addtoQueue(i*10);
    }

    pthread_exit(t);
}

void * readfromqueue(void* myqueue)
{
    void *t = 0;
    concurrentqueue *localqueue = (concurrentqueue *) myqueue;
    int number = 0;
    for ( int i = 0 ; i < 10 ; ++i)
    {
        number = localqueue->getFromQueue();
        std::cout << "The number from the queue is " << number << std::endl;
    }
    pthread_exit(t);
}

Ответы [ 2 ]

5 голосов
/ 24 августа 2010

Это определенно не безопасно:

if ( empty() ) 
{ 
    pthread_cond_wait(&queue_availability_condition,&lock_for_queue); 
} 

Если другой поток, который ранее не ожидал, вызывает getFromQueue() после addtoQueue(), сигнализировал переменную условия и завершил работу, но до того, как ожидающий поток получил блокировку, этот поток мог бы выйти и ожидать, что в очереди будут значения. Вы должны перепроверить, что очередь не пуста.

Измените if на некоторое время:

while ( empty() ) 
{ 
    pthread_cond_wait(&queue_availability_condition,&lock_for_queue); 
} 
2 голосов
/ 24 августа 2010

Изменение комментария Spong в качестве ответа: ваш класс locker НЕ ДОЛЖЕН копировать значение pthread_mutex_t по значению. Вместо этого вы должны использовать ссылку или указатель, например ::100100

class locker
{
public:
    locker(pthread_mutex_t& lockee): target(lockee)
    {
        pthread_mutex_lock(&target);
    }
    ~locker()
    {
        pthread_mutex_unlock(&target);
    }
private:
        pthread_mutex_t& target;  // <-- this is a reference
};

Причина этого в том, что все типы данных pthreads должны рассматриваться как непрозрачные типы - вы не знаете, что в них, и не должны их копировать. Библиотека делает такие вещи, как просмотр определенного адреса памяти, чтобы определить, удерживается ли блокировка, поэтому, если есть две копии переменной, указывающей, удерживается ли блокировка, могут происходить странные вещи, например, появление нескольких потоков, которые успешно блокируются тот же мьютекс.

Я проверил твой код, и он тоже для меня заблокирован. Затем я пропустил его через Valgrind , и хотя в этом случае он не зашел в тупик (из-за разного времени или, возможно, Valgrind имитирует только один поток за раз), Valgrind сообщил о многочисленных ошибках. После исправления locker для использования ссылки вместо этого он работал без блокировки и не генерировал никаких ошибок в Valgrind.

См. Также Отладка с помощью pthreads .

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