2011-12-27 97 views
1

我有生产者消费者问题需要稍加修改解决 - 有许多并行生产者,但只有一个消费者在一个并行线程。当制片人没有放置缓冲区时,它就会忽略元素(不等待消费者)。我有一些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%确定这段代码是正确的。你能看到可能发生的任何问题吗?或者,也许这个代码可以写得更好?

回答

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功能。

+0

我使用全局变量和原子操作执行无锁同步。这是出于性能原因。 – rnd 2011-12-27 11:10:34

+0

恩......实际上你只有增量安全。你仍然有缓冲[i] .b新的脏读。在我看来,使用单个信号量是一个更好的主意,而不是将生产者分配给数组中的单个位置索引 – SOReader 2011-12-27 12:16:53

0

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

C和C++标准没有解决多个线程(或多个 处理器),并且同样地,易失性的有用性取决于该 编译器和硬件。尽管易失性保证易失性写入和易失性写入将按照源代码 中指定的确切顺序发生,但编译器可能会生成代码(或者CPU可能会重新排序执行),以便对易失性读取或写入进行重新排序。 关于非易失性读取或写入,因此限制其作为线程间标志或互斥体的用处。此外,由于缓存,缓存一致性协议和轻松的内存排序,这意味着易失性读取和写入将被其他处理器以相同的 顺序看到,这意味着易失性变量本身甚至不可能作为线程间标记工作或互斥体。

因此,在通常情况下只挥发不会为C. 工作,但它可以为某些特定的编译器/硬件和其他工作语言(Java 5中,例如)。

又见Is function call a memory barrier?

+0

首先,互锁操作会使内存屏障。第二个问题是我在不需要内存屏障的x86上编译它(例如MemoryBarrier有空的主体) – rnd 2011-12-27 12:47:26

+0

那么多核高速缓存怎么样? – Vadzim 2011-12-27 13:06:19

+0

在x86上,所有更新都以写入顺序在其他内核上显示。如此不稳定就足以阻止重新排序。但我认为内容领域也应该是不稳定的,所以这可能是我的错误。 – rnd 2011-12-27 13:37:55