C 中的生产者-消费者线程安全环形缓冲区

4
在C语言中,我有多个线程生成long值,并且一个线程消费它们。因此,我需要一个固定大小的缓冲区,以类似于维基百科实现的方式实现,并且以线程安全的方式访问它的方法。
从一般层面上讲,应该遵循以下几点:
  • 当向满缓冲区添加内容时,线程应被阻塞(不要覆盖旧值)。
  • 消费者线程应该被阻塞,直到缓冲区已满-它的工作成本很高,应尽可能多地完成工作。(这是否需要双缓冲解决方案?)
我想使用经过验证的实现,最好是来自库。有什么建议吗?
动机和解释:
我正在编写处理在堆对象中保留的全局引用作为标签的JNI代码。
当发生ObjectFree JVMTI事件时,我会获得一个代表需要使用DeleteGlobalRef释放的全局引用的long标记。为此,我需要一个JNIEnv引用 - 而获取它是非常昂贵的,因此我想要缓冲请求并尽可能多地删除它们。
可能会有许多线程接收ObjectFree事件,并且将有一个线程(我的)执行引用删除。
3个回答

1

您可能需要考虑条件。看一下这个消费者的代码片段:

while( load == 0 )
    pthread_cond_wait( &notEmpty, &mutex );

它的作用是检查负载(存储列表中元素数量的位置)是否为零,如果是零,它将等待生产者生成新项目并将其放入列表中。
当生产者想要将项目放入满列表中时,您应该实现相同的条件。

1

您可以使用单个缓冲区,在访问时加锁。您需要跟踪使用了多少元素。对于“信号”,您可以使用条件变量。其中一个由生产者线程触发,每当它们将数据放入队列中时;这会释放消费者线程以处理队列直到为空。另一个由消费者线程触发,当它清空队列时;这会向任何被阻塞的生产者线程发出信号以填充队列。对于消费者,我建议在释放锁之前锁定队列并尽可能多地取出(以避免太多锁),特别是因为出队操作简单且快速。

更新
一些有用的链接:
* 维基百科解释
* POSIX线程
* MSDN


1

有两种可能性:

a) 使用malloc()函数分配一个*Buffer结构体,其中包含一个数组来保存一些长整型和一个索引 - 不需要锁定。每个生产者线程都要malloc()自己的*Buffer并开始加载数据。当生产者线程填满最后一个数组位置时,将*Buffer排队到生产者-消费者队列上,并立即malloc()一个新的*Buffer。消费者获取*Buffers并处理它们,然后free()它们(或将它们排队到其他地方,或将它们推回到池中以供生产者重新使用)。这避免了对缓冲区本身的任何锁定,只留下P-C队列上的锁定。问题在于,只偶尔生成长整型的生产者直到其*Buffer被填满才能处理其数据,这可能需要一些时间(在这种情况下,您可以在数组填满之前将缓冲区推迟)。

b) 声明一个Buffer结构体,其中包含一个长整型数组和一个索引。使用互斥锁/条件变量/临界区进行保护。只malloc()一个共享的*Buffer,并让所有线程获取锁,在其长整型数据上执行push操作,然后释放锁。如果一个线程在最后一个数组位置上执行push操作,则将*Buffer排队到生产者-消费者队列上,立即malloc()一个新的*Buffer,然后释放锁。消费者获取*Buffers并处理它们,然后free()它们(或将它们排队到其他地方,或将它们推回到池中以供生产者重新使用)。

你好,谢谢。不幸的是我的C语言水平不够,不确定你的答案是否真正适用于C语言——它似乎是针对C++的。你能否请澄清一下? - vektor
1
糟糕!!!!!!在编辑中,我会将“class”替换为“struct”,将“new”替换为“malloc()”,将“delete()”替换为“free()”。我的错误:(( - Martin James

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接