对于单个生产者/消费者循环队列实现,实际上不需要锁。只需设置一个条件,即如果队列已满,则生产者无法写入队列,如果队列为空,则消费者无法从队列中读取。此外,生产者将始终写入tail
指向队列中第一个可用空槽的指针,而消费者将从head
表示队列中第一个未读槽的指针中读取。
您的代码可以看起来像下面的代码示例(注意:我假设在一个初始化队列中tail == head
,并且声明了两个指针,volatile
以便优化编译器不会重新排序给定线程中的操作序列。在 x86 上,由于架构的强内存一致性模型,不需要内存屏障,但这会在内存一致性模型较弱的其他架构上发生变化,其中需要内存屏障):
queue_type::pointer queue_type::next_slot(queue_type::pointer ptr);
bool queue_type::enqueue(const my_type& obj)
{
if (next_slot(tail) == head)
return false;
*tail = obj;
tail = next_slot(tail);
return true;
}
bool queue_type::dequeue(my_type& obj)
{
if (head == tail)
return false;
obj = *head;
head = next_slot(head);
return true;
}
该函数next_slot
只是增加head
ortail
指针,以便它返回指向数组中下一个槽的指针,并考虑任何数组环绕功能。
最后,我们保证单生产者/消费者模型中的同步,因为在tail
指针将数据写入它所指向的插槽之前我们不会递增指针,并且在head
我们从插槽中读取数据之前我们不会递增指针指着。因此,dequeue
在至少调用一次之前,调用不会返回有效enequeue
,并且tail
指针永远不会head
因为签入而覆盖指针enqueue
。此外,只有一个线程递增tail
指针,一个线程递增head
指针,因此从或向同一指针共享读取或写入不会产生同步问题,需要锁定或某种类型的原子操作。