8

假设我们有一个共享队列(使用数组实现),两个线程可以访问该队列,一个用于从中读取数据,另一个用于向其写入数据。现在,我有一个同步问题。我正在使用 Win32 API(EnterCriticalSection 等)来实现这一点。

但我的好奇心是队列的入队和出队操作中的关键部分代码是什么?

仅仅因为,两个线程正在使用共享资源?为什么我看不到任何问题是:前后端都维护了,所以当ReaderThread读取时,它可以从前端读取,而当WriterThread写入时,它可以轻松地写入后端。

可能会出现哪些潜在问题?

4

1 回答 1

6

对于单个生产者/消费者循环队列实现,实际上不需要锁。只需设置一个条件,即如果队列已满,则生产者无法写入队列,如果队列为空,则消费者无法从队列中读取。此外,生产者将始终写入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只是增加headortail指针,以便它返回指向数组中下一个槽的指针,并考虑任何数组环绕功能。

最后,我们保证单生产者/消费者模型中的同步,因为在tail指针将数据写入它所指向的插槽之前我们不会递增指针,并且在head我们从插槽中读取数据之前我们不会递增指针指着。因此,dequeue在至少调用一次之前,调用不会返回有效enequeue,并且tail指针永远不会head因为签入而覆盖指针enqueue。此外,只有一个线程递增tail指针,一个线程递增head指针,因此从或向同一指针共享读取或写入不会产生同步问题,需要锁定或某种类型的原子操作。

于 2011-07-27T04:48:47.100 回答