2016-01-15 63 views
3

在操作系統概念(Silberschatz,第9版)第3.4.1,作者提出了生產者 - 消費者問題,並給出了使用循環緩衝區(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. 最明顯的:如果有2點生產商生產的數據。假設只有1緩衝區自由空間,無論是生產者線程可以得到通過while (in + 1) % BUFFER SIZE) == out並把它放在緩衝區。這可能會導致損壞的緩衝區或丟失數據

  2. 即使只有1消費者和生產者1,仍然有一些不太明顯的問題。例如,編譯器可能會重新安排線路

    buffer[in] = next_produced; 
    in = (in + 1) % BUFFER SIZE; 
    

    做出的in更新發生比buffer,這引起了消費者訪問未初始化的數據更新前面。

+0

請詳細說明一下#2'編譯器可能會重新安排lines'。簡單地交換行會顯然產生不同的行爲,所以我猜你寧願意味着沿着「編譯器可能將其重新排列爲'{int temp = in; in =(in + 1)%BUFFER SIZE; buffer [temp] = next_produced;}'「。然而,如果'in'是一個超出邊界的索引(這可能導致例如拋出異常),則這種重寫將具有不同的UB。在原始代碼中,UB處的「in」將是無效的,在重寫時它將是有效的。 C標準真的允許這樣的重寫嗎? – dxiv

+0

相當多的語言允許編譯器爲了優化而重新排列指令。只是谷歌的「C++編譯器重新排序指令優化」會給你一些信息。 –

+0

@dxiv另外,看看https://en.wikipedia.org/wiki/Memory_ordering和https://en.wikipedia.org/wiki/Consistency_model – jyvet

2

沒有保證到buffer[x]寫就的inout

因此,假設只有一個閱讀器和一個作家,那麼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之前的值已讀出的值。