在操作系统概念的第3.4.1节(Silberschatz,第9版)中,作者介绍了生产者 - 消费者问题,并给出了以下使用循环缓冲区的实现(第125,126页)。
//Shared variables
#define BUFFER SIZE 10
struct Item;
Item buffer[BUFFER SIZE];
int in = 0, out = 0;
//buffer is empty when in == out
//buffer is full when (in + 1) % BUFFER SIZE == out
//Producer
while (true)
{
Item next_produced = /*produce item here*/;
while (((in + 1) % BUFFER SIZE) == out) ; //do nothing
buffer[in] = next_produced;
in = (in + 1) % BUFFER SIZE;
}
//Consumer
while (true)
{
while (in == out) ; //do nothing
Item next_consumed = buffer[out];
out = (out + 1) % BUFFER SIZE;
//consume the item in next_consumed here
}
这本书说:
这个例子中的一个问题并没有解决有关情况的问题 生产者流程和消费者流程都试图这样做 同时访问共享缓冲区。
我没有看到生产者和消费者同时访问相同缓冲元素的情况。
我的问题是:如果生产者和消费者在两个线程中运行,那么这个实现中是否存在竞争条件或其他同步问题?
答案 0 :(得分:2)
有很多可能性
最明显的:如果有2个生产者生成数据。假设缓冲区中只有1个可用空间,则两个生成器线程都可以通过while (in + 1) % BUFFER SIZE) == out
并尝试放入缓冲区。这可能导致缓冲区损坏或数据丢失
即使只有1个消费者和1个生产者,仍然存在一些不太明显的问题。例如,编译器可能会重新排列行
buffer[in] = next_produced;
in = (in + 1) % BUFFER SIZE;
使in
的更新发生在更新buffer
之前,这会导致消费者访问未初始化的数据。
答案 1 :(得分:2)
无法保证在修改buffer[x]
或in
out
的写入
因此假设只有一个读者和一个编写者,那么in,out变量都在一个线程中被修改。
buffer[in] = next_produced;
in = (in + 1) % BUFFER SIZE;
可以看到读者错误排序,导致读者看到移动,但缓冲区的旧值[in]
Item next_consumed = buffer[out];
out = (out + 1) % BUFFER SIZE;
编译器或处理器可能会造成错误,允许生产者在buffer[out]
读取值之前写入完整队列覆盖next_consumed
的值。