【问题标题】:Boost condition deadlock using wait() in producer-consumer code在生产者-消费者代码中使用 wait() 提升条件死锁
【发布时间】:2011-04-25 03:28:54
【问题描述】:

我已经使用 Boost 线程和条件实现了一个基本的线程生产者-消费者(线程 1 = 生产者,线程 2 = 消费者)。我经常无限期地陷入 wait() 中。我真的看不出这里有什么问题。下面是一些伪代码:

// main class
class Main {
public:
  void AddToQueue(...someData...)
  {
    boost::mutex::scoped_lock lock(m_mutex);
    m_queue.push_back(new QueueItem(...someData...));
    m_cond.notify_one(); 
  }

  void RemoveQueuedItem(...someCond...)
  {
    // i'm wondering if this could cause the trouble?
    boost::mutex::scoped_lock lock(m_mutex);
    // erase a item matching condition (some code not shown,
    // but should be fairly self-explanatory -- IsMatch()
    // simply looks at a flag of QueueItem
    m_queue.erase(std::remove_if(m_queue.being(), m_queue.end(),
      boost::bind(&Main::IsMatch, this, _1, someCond), m_queue.end());
  }

  friend void WorkerThread(Main* m);
private:      
  boost::ptr_deque<QueueItem> m_queue;
  boost::mutex m_mutex;
  boost::condition m_cond;
};

// worker thread
void WorkerThread(Main* m)
{
  typedef boost::ptr_deque<QueueItem>::auto_type RelType;
  RelType queueItem;

  while(!shutDown) {
    { // begin mutex scope
      boost::mutex::scoped_lock lock(m->m_mutex);
      while(m->m_queue.empty()) {
        m->m_cond.wait(lock); // <- stuck here forever quite often!
      }
      queueItem = m->m_queue->pop_front(); // pop & take ptr ownership
    } // end mutex scope

    // ... do stuff with queueItem
    // ...
    // ... queueItem is deleted when it leaves scope & we loop around
  }
}

一些附加信息:

  • 使用 Boost v1.44
  • 在 Linux 和 Android 中出现问题;我还不确定它是否发生在 Windows 中

有什么想法吗?

更新: 我相信我已经隔离了这个问题。确认后我会进一步更新,希望是明天。

更新 2: 事实证明,上述代码没有问题。我依赖于 AddToQueue() 的底层 API - 在工作线程中处理数据并将其返回给 API 时,它有一个循环错误,它会再次调用 AddToQueue() ......现在已修复;-)

【问题讨论】:

  • 您上面的代码缺少您在条件变量上等待的部分。也粘贴一下。
  • 在我上面的工作线程(消费者)中,你会看到一段时间(queue_is_empty()) { cond.wait(lock); } 你指的是这个吗?

标签: c++ boost conditional-statements wait producer-consumer


【解决方案1】:

我最近做了类似的事情,尽管我使用的是 STL 队列。看看你能不能从我的实现中挑选出来。正如wilx 所说,您需要等待条件。我的实现对队列中的元素有最大限制,我用它来等待互斥锁/保护被释放。

我最初在 Windows 上执行此操作时考虑到了使用 Mutex 或关键部分的能力,因此您可以删除模板参数并直接使用 boost::mutex 如果它为您简化它。

#include <queue>
#include "Message.h"
#include <boost/thread/locks.hpp>
#include <boost/thread/condition.hpp>

template <typename T> class Queue :  private boost::noncopyable
{
public:
  // constructor binds the condition object to the Q mutex
  Queue(T & mutex, size_t max_size) :  m_max_size(max_size), m_mutex(mutex){}

  // writes messages to end of Q 
  void put(const Message & msg)
  {
    // Lock mutex to ensure exclusive access to Q
    boost::unique_lock<T> guard(m_mutex);

    // while Q is full, sleep waiting until something is taken off of it
    while (m_queue.size() == m_max_size)
    {
      cond.wait(guard);
    }

    // ok, room on the queue. 
    // Add the message to the queue
    m_queue.push(msg);

    // Indicate so data can be ready from Q
    cond.notify_one();
  }

  // Read message from front of Q. Message is removed from the Q
  Message get(void)
  {
    // Lock mutex to ensure exclusive access to Q
    boost::unique_lock<T> guard(m_mutex);

    // If Q is empty, sleep waiting for something to be put onto it
    while (m_queue.empty())
    {
      cond.wait(guard);
    }

    // Q not empty anymore, read the value
    Message msg = m_queue.front();

    // Remove it from the queue
    m_queue.pop();

    // Signal so more data can be added to Q
    cond.notify_one();

    return msg;
  }

  size_t max_size(void) const
  {
    return m_max_size;
  }


private:
  const size_t m_max_size;
  T & m_mutex;
  std::queue<Message> m_queue;
  boost::condition_variable_any cond;
};

这样,您可以在生产者/消费者之间共享队列。示例用法

boost::mutex mutex;

Queue<boost::mutex> q(mutex, 100);

boost::thread_group threads;

threads.create_thread(Producer<boost::mutex>(q));
threads.create_thread(Consumer<boost::mutex>(q));

threads.join_all();

生产者/消费者定义如下

template <typename T> class Producer
{
public:
   // Queue passed in
   explicit Producer(Queue<T> &q) :  m_queue(q) {}

   void operator()()
   {
   }
}

【讨论】:

  • 原来我的代码的生产者-消费者部分没有错误(稍后将在主帖中发布详细信息)。不过我真的很喜欢你的实现,我想我可能会重新考虑我的一些类似的东西。
  • 干杯。很高兴做出贡献。
【解决方案2】:
m->m_cond.wait(); // <- stuck here forever quite often!

应该是:

m->m_cond.wait( lock ); 

您已经完全锁定了您的课程,因为您仍然获得了互斥锁,但您正在等待。所有其他方法都想获取相同的互斥锁并等待永远不会释放互斥锁的工作人员。

【讨论】:

  • 对不起,这只是我的伪代码中的一个错字。我已经在上面更正了。实际代码已实现等待实际锁定 (boost::mutex::scoped_lock)。
  • 好的,出于好奇,请发布代码以向队列中添加内容。
  • 它已经作为 Queue() 存在。为了清楚起见,我已将上面伪代码中的函数重命名为 AddToQueue()。
猜你喜欢
  • 2013-03-06
  • 2011-05-04
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多