C ++ 11非阻塞生产者/消费者

时间:2014-01-28 19:27:53

标签: multithreading c++11 producer-consumer

我有一个C ++ 11应用程序,它具有一个产生数据的高优先级线程,以及一个消耗它的低优先级线程(在我的情况下,将其写入磁盘)。我想确保高优先级的生产者线程永远不会被阻止,即它只使用无锁算法。

使用无锁队列,我可以从生产者线程将数据推送到队列,并从消费者线程中轮询它,从而实现上述目标。我想修改我的程序,以便消费者线程在非活动时阻塞而不是轮询。

似乎C ++ 11条件变量可能对阻止使用者线程很有用。有人能告诉我一个如何使用它的例子,同时避免消费者睡觉时数据仍在队列中吗?更具体地说,我想确保在生产者将最后一个项目推入队列后,消费者总是在某个有限的时间内被唤醒。生产者保持非阻塞也很重要。

3 个答案:

答案 0 :(得分:1)

  

似乎C ++ 11条件变量可能对阻止使用者线程很有用。任何人都可以向我展示一个如何使用它的例子,同时避免消费者睡觉时数据仍然在队列中吗?

要使用条件变量,您需要互斥锁和条件。在您的情况下,条件将是“队列中有可用的数据”。由于生产者将使用无锁更新来生成工作,因此消费者必须使用相同形式的同步来使用工作,因此互斥体实际上不会用于同步,只有消费者线程才需要它,因为没有等待条件变量的其他方式。

// these variables are members or otherwise shared between threads
std::mutex m_mutex;
std::condition_variable m_cv;
lockfree_queue m_data;

// ...

// in producer thread:
while (true)
{
  // add work to queue
  m_data.push(x);
  m_cv.notify_one();
}

// in consumer thread:
while (true)
{
  std::unique_lock<std::mutex> lock(m_mutex);
  m_cv.wait(lock, []{ return !m_data.empty(); });
  // remove data from queue and process it
  auto x = m_data.pop();
}

如果在等待之前队列为空,则条件变量将仅在wait调用中阻塞。条件变量可能是虚假唤醒,或者是因为它是由生产者通知的,但是在任何一种情况下,如果队列非空,则只会从wait调用返回(而不是再次休眠)。通过使用带有谓词的condition_variable::wait重载来保证这一点,因为条件变量总是为你重新检查谓词。

由于互斥体线程仅使用互斥体,因此它实际上可能是该线程的本地线程(只要您只有一个消费者,只要有多个消费者,他们都需要共享相同的互斥体以等待相同的condvar )。

答案 1 :(得分:0)

我过去发现的一个解决方案是使用Windows事件(http://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx)。在这种情况下,事件仍会发出信号,直到它唤醒等待的线程,如果没有线程正在等待,它将保持信号状态。因此,生产者只需要在将数据推送到队列后发出信号。然后我们保证在此之后消费者会在一段有限的时间内醒来。

我无法找到使用标准库实现此方法的方法(至少不会阻止生产者线程)。

答案 2 :(得分:0)

我认为信号量可以用来安全地解决这个问题:

// in producer thread:
while (true)
{
  m_data.push();
  m_semaphore.release();
}

// in consumer thread:
while (true)
{
  m_semaphore.wait();
  m_data.pop();
}

不幸的是我不认为C ++ 11包含信号量?我也无法确认释放信号量是一种非阻塞操作。当然可以使用互斥锁实现,例如C++0x has no semaphores? How to synchronize threads?不允许使用非阻塞生成器线程。