2013-11-22 75 views
0

我有一個場景,我可以有多個生產者,但環形緩衝區的單個消費者。這裏是源代碼:等待免費環形緩衝區

typedef struct ring_buffer_t { 
    uint8_t *data; 
    uint32_t element_size; 
    uint32_t element_count; 
    uint32_t head; 
    uint32_t tail; 
    uint32_t mask; 
} ring_buffer; 

uint32_t ring_buffer_put(ring_buffer *buf, void *data_element) { 
    int i; 
    uint32_t status = 0; 
    uint8_t *buf_pointer; 
    uint8_t *element = (uint8_t *) data_element; 

    if (buf && data_element) { 

     buf_pointer = &buf->data[(buf->head & buf->mask) * buf->element_size]; 
     for (i = 0; i < buf->element_size; ++i) { 
      buf_pointer[i] = element[i]; 
     } 

     status = 1; 
     __sync_fetch_and_add(&buf->head, 1); 

    } 

    return status; 
} 

uint32_t ring_buffer_size(ring_buffer *buf) { 

    return buf->head - buf->tail; 
} 

uint32_t ring_buffer_empty(ring_buffer *buf) { 

    return (ring_buffer_size(buf) == 0); 
} 

void *ring_buffer_get(ring_buffer *buf) { 
    void *element; 
    //preserve the invariant that tail is always <= head 
    if (ring_buffer_empty(buf)) { 
     return 0; 
    } 

    element = &buf->data[(buf->tail & buf->mask) * buf->element_size]; 
    __sync_fetch_and_add(&buf->tail, 1); 
    return element; 
} 

問題陳述允許我覆蓋舊的條目,例如這就是爲什麼我使用and和沒有檢查緩衝區是否已滿時放 - 我只覆蓋最舊的條目。但是,我很難推斷這是否是線程安全的。如前所述 - 重要的是這是無等待的(根據定義分別是無鎖),因爲生產商無法承受阻塞。大小將始終是2的冪,因此這就是爲什麼索引期間&工作。

任何輸入將不勝感激?

+0

我不明白它是如何可以線程安全的,__sync_fetch_and_add確實是原子的,但是什麼可以防止競爭條件?我沒看到。 –

+0

唯一的無等待環形緩衝區實現是當緩衝區溢出時調用exit(1)的實現。 –

+0

從技術上講,緩衝區永遠不會變滿,因爲覆蓋舊條目完全可以。 – LordDoskias

回答

0

不,您的實現不是線程安全的。

如果兩個線程put元素同時它們可以寫在相同的位置,甚至在一個位置的一半記錄,另一半在下一個位置。

+2

這對多生產者,單一消費者來說確實是不安全的。雖然它將適用於單一消費者,單一生產者。爲了使它對SCMP是線程安全的,我不得不採取類似於什麼干擾者正在做的事情http://blog.codeaholics.org/2011/the-disruptor-lock-free-publishing/ – LordDoskias