两个线程访问的共享队列的关键部分代码是什么?

Raj*_*pal 8 c++ multithreading critical-section

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

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

仅仅因为,两个线程正在使用共享资源?为什么我无法看到任何问题:前端和后端都被维护,因此,当ReaderThread读取时,它可以从前端读取,当WriterThread写入时,它可以轻松写入后端.

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

Jas*_*son 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;
}
Run Code Online (Sandbox Code Playgroud)

该函数next_slot只是递增headtail指针,以便它返回指向数组中下一个插槽的指针,并说明任何数组环绕功能.

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

  • @sad_man:如果队列已满/空并且使用事件唤醒相应的线程,则可以阻塞enqueue/dequeue上的生产者/消费者. (2认同)