【问题标题】:Why no need to synchronize buffer when only have one producer and one consumer?为什么只有一个生产者和一个消费者时不需要同步缓冲区?
【发布时间】:2017-11-05 17:33:53
【问题描述】:

以下伪代码来自Wiki - producer-consumer problem

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);
    }
}

我的问题是,为什么我们可以同时调用 putItemIntoBufferremoveItemFromBuffer 而无需同步?为什么这里没有竞态条件?

【问题讨论】:

  • 为什么你认为没有竞争条件?只有一个生产者和一个消费者的正确操作取决于 putItemIntoBuffer 和 removeItemFromBuffer 的实现。它可以是安全的..或不。对于多个生产者/消费者,您肯定需要锁定队列。

标签: multithreading operating-system semaphore race-condition producer-consumer


【解决方案1】:

为什么我们可以同时调用 putItemIntoBuffer 和 removeItemFromBuffer 而不同步?

简短的回答:它是一个伪代码,所以不要从字面上理解它。代码的目的是展示如何同步消费者和生产者,而不是数据访问,就像维基文章开头所说的那样:

问题是确保生产者在缓冲区已满时不会尝试将数据添加到缓冲区中,并且消费者不会尝试从空缓冲区中删除数据。

您所指的伪代码使用信号量解决了这个问题:

信号量解决了丢失唤醒呼叫的问题。

如果缓冲区大小为 1,则代码可以正常工作而无需任何额外的麻烦。但是对于任何缓冲区大小的 putItemIntoBuffer()/removeItemFromBuffer() 的实际实现可能会在内部使用锁来访问共享缓冲区,或者可以实现为无锁循环队列,就像下面显示的 producer()/consumer() 函数之一文章,即:

volatile unsigned int produceCount = 0, consumeCount = 0;
ItemType buffer[BUFFER_SIZE];

void putItemIntoBuffer(ItemType *item) {
    buffer[produceCount % BUFFER_SIZE] = *item;
    ++produceCount;
}
void removeItemFromBuffer(ItemType *item) {
    *item = buffer[consumeCount % BUFFER_SIZE];
    ++consumeCount;
}

为什么这里没有竞争条件?

访问共享缓冲区可能存在竞争条件,但这不是生产者-消费者问题的重点,因此也不是上述 Wiki 文章的重点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-18
    • 1970-01-01
    • 2017-04-07
    • 2016-09-03
    • 1970-01-01
    • 2011-12-16
    • 1970-01-01
    相关资源
    最近更新 更多