这个生产者 – 消费者实施中是否存在竞争条件?
在操作系统概念的第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 }
这本书说:
此示例的一个问题不涉及生产者进程和使用者进程同时尝试访问共享缓冲区的情况。
我没有看到生产者和消费者同时访问相同缓冲元素的情况。
我的问题是:如果生产者和消费者在两个线程中运行,那么这个实现中是否存在竞争条件或其他同步问题?
有很多可能性
-
最明显的是:如果有2个生产者生产数据。 假设缓冲区中只有1个可用空间,两个生产者线程都可以通过
while (in + 1) % BUFFER SIZE) == out
并尝试放入缓冲区。 这可能导致缓冲区损坏或数据丢失 -
即使只有1个消费者和1个生产者,仍然存在一些不太明显的问题。 例如,编译器可能会重新排列行
buffer[in] = next_produced; in = (in + 1) % BUFFER SIZE;
使更新发生在
buffer
更新之前,这会导致消费者访问未初始化的数据。
无法保证在修改in
或out
之前可以看到对buffer[x]
的写入
因此,假设只有一个读取器和一个写入器,则in,out变量分别在单个线程中进行修改。
buffer[in] = next_produced; in = (in + 1) % BUFFER SIZE;
可以看到在读者中错误排序,导致读者看到移动,但缓冲区的旧值[在]
Item next_consumed = buffer[out]; out = (out + 1) % BUFFER SIZE;
可能会被编译器或处理器next_consumed
,允许生产者写入完整队列,在next_consumed
读取值之前覆盖buffer[out]
的值。