为什么生产者消费者与单个生产者/消费者排队不需要互斥体?

时间:2018-12-18 13:50:05

标签: parallel-processing synchronization mutex semaphore producer-consumer

具有单个生产者和单个消费者的生产者消费者队列的代码From wikipedia为:

semaphore fillCount = 0; // items produced
semaphore emptyCount = BUFFER_SIZE; // remaining space

procedure producer() 
{
    while (true) 
    {
        item = produceItem();
        down(emptyCount);
        putItemIntoBuffer(item);
        up(fillCount);
    }
}

procedure consumer() 
{
    while (true) 
    {
        down(fillCount);
        item = removeItemFromBuffer();
        up(emptyCount);
        consumeItem(item);
    }
}

在那里说

  

当只有一个生产者并且   消费者。

当生产者/消费者更多时,伪代码是相同的,互斥体保护着putItemIntoBuffer(item);removeItemFromBuffer();部分:

mutex buffer_mutex; // similar to "semaphore buffer_mutex = 1", but different (see notes below)
semaphore fillCount = 0;
semaphore emptyCount = BUFFER_SIZE;

procedure producer() 
{
    while (true) 
    {
        item = produceItem();
        down(emptyCount);
        down(buffer_mutex);
        putItemIntoBuffer(item);
        up(buffer_mutex);
        up(fillCount);
    }
}

procedure consumer() 
{
    while (true) 
    {
        down(fillCount);
        down(buffer_mutex);
        item = removeItemFromBuffer();
        up(buffer_mutex);
        up(emptyCount);
        consumeItem(item);
    }
}

我的问题是,为什么单一生产者单一消费者情况下不需要互斥体?

请考虑以下内容:

  1. 队列中有5个项目,允许10个项目。
  2. 生产者产生一个项目,减少空信号量(并成功),然后开始将该项目放入缓冲区,但尚未完成
  3. 消费者减少填充信号量,然后开始从缓冲区中删除项目
  4. 意外。尝试将项目从缓冲区(2)移出缓冲区(2)

为什么我的描述没有发生?

1 个答案:

答案 0 :(得分:1)

因为这样的队列通常将实现为循环队列。生产者将写在队列的尾部,而消费者则从头读。他们永远不会同时访问相同的内存。

这里的想法是消费者和生产者都可以独立跟踪尾巴/头部的位置。

考虑以下伪代码:

T data[BUFFER_SIZE];
int producerPtr = 0, consumerPtr = 0;

void putItemIntoBuffer(Item item)
{
     data[producerPtr] = item;
     producerPtr = (producerPtr  + 1) % BUFFER_SIZE;
}

Item removeItemFromBuffer(void)
{
     Item item = data[consumerPtr ];
     consumerPtr = (consumerPtr + 1) % BUFFER_SIZE;
     return item;
}

consumerPtrproducerPtr只能在队列已满或为空时相等,在这种情况下,这些函数将不会被调用,因为执行过程将一直保持在信号量上。

您可以说信号量被用作消息传递机制,从而允许另一端增加其指针,使其同步。

现在,如果您在一侧有多个进程,则该侧将需要原子执行增量和数据复制,因此需要互斥体,但仅对于具有多个进程的那一侧(例如,对于多生产者和多消费者队列,您可以使用2个独立的互斥锁来减少争用。