【问题标题】:Why this thread safe queue, creates a deadlock?为什么这个线程安全队列会造成死锁?
【发布时间】:2010-08-24 00:18:24
【问题描述】:

我已经编写了我自己的线程安全队列版本。但是,当我运行这个程序时,它会自行挂起/死锁。

想知道,为什么这会永远锁定/挂起。

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

【问题讨论】:

  • 无论如何我都不是pthreads 专家,但我认为您不想将pthread_mutex_t 按值复制到target 中的locker 课程中。将target 设为pthread_mutex_t &amp;pthread_mutex_t *
  • @spong 我确定这是问题所在。通过制作副本,锁定和信号不再处理相同的结构,这将导致许多 pthread 实现变得不稳定。您应该添加您的评论作为答案。
  • 为什么要重新发明轮子。这是我使用的实现。 gist.github.com/482342
  • 洛根先生,您说得对。感谢您解决问题。
  • Mr.Logan,我该如何给你的正确答案。您介意发布相同的答案,以便我可以选择您的答案。

标签: c++ multithreading stl pthreads


【解决方案1】:

这绝对不安全:

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

如果另一个以前没有等待的线程在addtoQueue() 发出条件变量信号并退出之后调用getFromQueue(),但在等待线程获得锁之前,则该线程可以退出并期望队列中有值。您必须重新检查队列是否为空。

把if改成while:

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

【讨论】:

  • 这是死锁的原因吗?
  • @nsivakr,这可能是许多问题的原因,在你的情况下,在一个空列表上调用 front() ,但我不认为这不是你死锁的原因。我相信 spong 有这个权利。
【解决方案2】:

将 spong 的评论重新表述为答案:您的 locker 类不应按值复制 pthread_mutex_t。您应该改用引用或指针,例如:

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 中产生任何错误。

另见Debugging with pthreads

【讨论】:

  • 谢谢亚当。感谢您的回复。
猜你喜欢
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
  • 2021-07-13
  • 1970-01-01
  • 2010-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多