Why this thread safe queue, creates a deadlock?

本小妞迷上赌 提交于 2019-12-23 05:30:27

问题


I've written my own version of thread safe queue. However, when I run this program, it hangs/deadlocks itself.

Wondering, why is this locks/hangs forever.

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

I've written, the class locker as 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;
};

My writer/reader thread code is very simple. Writer thread, adds to the queue and reader thread, reads from the queue.

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

回答1:


Reformulating spong's comment as an answer: your locker class should NOT be copying the pthread_mutex_t by value. You should use a reference or a pointer instead, e.g.:

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

The reason for this is that all pthreads data types should be treated as opaque types -- you don't know what's in them and should not copy them. The library does things like looking at a particular memory address to determine if a lock is held, so if there are two copies of a variable that indicates if the lock is held, odd things could happen, such as multiple threads appearing to succeed in locking the same mutex.

I tested your code, and it also deadlocked for me. I then ran it through Valgrind, and although it did not deadlock in that case (due to different timings, or maybe Valgrind only simulates one thread at a time), Valgrind reported numerous errors. After fixing locker to use a reference instead, it ran without deadlocking and without generating any errors in Valgrind.

See also Debugging with pthreads.




回答2:


This is definitely not safe:

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

If another thread that was not previously waiting calls getFromQueue() after addtoQueue() has signalled the condition variable and exited but before the waiting thread has aquired the lock then this thread could exit and expect the queue to have values in it. You must recheck that the queue is not empty.

Change the if into a while:

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


来源:https://stackoverflow.com/questions/3552677/why-this-thread-safe-queue-creates-a-deadlock

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!