几乎无锁的生产者消费者

时间:2011-12-27 10:50:29

标签: thread-safety producer-consumer lockless

我有生产者消费者问题需要稍作修改才能解决 - 有许多并行生产者,但在一个并行线程中只有一个消费者。当生产者在缓冲区中没有位置时,它只是忽略元素(不等待消费者)。我写了一些C伪代码:

struct Element
{
   ULONG content;
   volatile LONG bNew;
}

ULONG max_count = 10;
Element buffer* = calloc(max_count, sizeof(Element));
volatile LONG producer_idx = 0;
LONG consumer_idx = 0;
EVENT NotEmpty;

BOOLEAN produce(ULONG content)
{
  LONG idx = InterlockedIncrement(&consumer_idx) % max_count;

  if(buffer[idx].bNew)
    return FALSE;
  buffer[idx].content = content;
  buffer[idx].bNew = TRUE;
  SetEvent(NotEmpty);
  return TRUE;
}

void consume_thread()
{
  while(TRUE)
  {
    Wait(NotEmpty);
    while(buffer[consumer_idx].bNew)
    {
      ULONG content = buffer[consumer_idx].content;
      InterlockedExchange(&buffer[consumer_idx].bNew, FALSE);
      //Simple mechanism for preventing producer_idx overflow
      LONG tmp = producer_idx;
      InterlockedCompareExchange(&producer_idx, tmp%maxcount, tmp);
      consumer_idx = (consumer_idx+1)%max_count;
      doSth(content);
    }
  }
}

我不是100%确定此代码是正确的。你能看到可能出现的问题吗?或者也许这段代码可以用更好的方式编写?

2 个答案:

答案 0 :(得分:0)

不要使用全局变量来实现您的目标,尤其是在多线程应用程序中!请改用Semaphore而不是Lock,而是使用TryLock。如果TryLock失败,则意味着没有其他元素的空间,所以你可以跳过它。

在这里,您可以在WinAPI中找到有关信号量的内容,因为您可能会使用它: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686946(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx

您可以通过将0作为超时传递给WaitForSingleObject函数来实现TryLock功能。

答案 1 :(得分:0)

请阅读:http://en.wikipedia.org/wiki/Memory_barrier

  

C和C ++标准不涉及多个线程(或多个线程)   处理器),因此,volatile的用处取决于   编译器和硬件。虽然挥发性保证了挥发性   读取和易失性写入将按照指定的确切顺序发生   源代码,编译器可能生成代码(或CPU可能   重新排序执行)以便重新排序易失性读或写   关于非易失性读或写,从而限制其   作为线程间标志或互斥的有用性。而且,事实并非如此   保证可以看到易失性读写   由于缓存,缓存一致性协议和其他处理器的顺序   放松的记忆排序,意味着单独的易变变量可能不均匀   作为线程间标志或互斥体工作。

所以在一般情况下,只有volatile不适用于C. 但它可以用于某些特定的编译器/硬件和其他语言(例如Java 5)。

另见Is function call a memory barrier?