2013-09-28 92 views
1

我是新來的整個循環/環緩衝思維方式。我閱讀了一些關於它如何在理論上工作的文章,並提出了這個代碼示例。在我的場景中,我將有多個線程寫入和從緩衝區讀取單個線程。CircularBuffer,我需要添加鎖嗎?

我需要一個鎖添加到寫入方法?

在此先感謝!

public class CircularBuffer<T> 
{ 
    private readonly int _size; 

    private int _head; 
    private byte _headMirrorSide; 

    private int _tail; 
    private byte _tailMirrorSide; 

    private readonly T[] _buffer; 

    public CircularBuffer() : this(300) { } 

    public CircularBuffer(int size) 
    { 
     _size = size; 
     _buffer = new T[_size + 1]; 

     _head = 0; 
     _headMirrorSide = 0; 

     _tail = 0; 
     _tailMirrorSide = 0; 
    } 

    private bool IsFull() 
    { 
     return _tail == _head && _tailMirrorSide != _headMirrorSide; 
    } 

    public bool IsEmpty() 
    { 
     return _tail == _head && _tailMirrorSide == _headMirrorSide; 
    } 

    private void MovePointer(ref int pointer, ref byte mirrorSide) 
    { 
     pointer = pointer + 1; 

     if (pointer == _size) 
     { 
      mirrorSide ^= 1; 
      pointer = 0; 
     } 
    } 

    public void Write(T obj) 
    { 
     _buffer[_head] = obj; 

     if (IsFull()) 
     { 
      MovePointer(ref _tail, ref _tailMirrorSide); 
     } 
     MovePointer(ref _head, ref _headMirrorSide); 
    } 

    public T Read() 
    { 
     var obj = _buffer[_tail]; 
     _buffer[_tail] = default(T); 

     MovePointer(ref _tail, ref _tailMirrorSide); 

     return obj; 
    } 
} 

編輯:最終的結果是這樣的。

public class CircularBuffer<T> where T : class 
{ 
    private readonly int _size; 

    private int _head; 
    private byte _headMirrorSide; 

    private int _tail; 
    private byte _tailMirrorSide; 

    private readonly T[] _buffer; 

    private readonly object _lock = new object(); 

    public CircularBuffer() : this(300) { } 

    public CircularBuffer(int size) 
    { 
     _size = size; 
     _buffer = new T[_size + 1]; 

     _head = 0; 
     _headMirrorSide = 0; 

     _tail = 0; 
     _tailMirrorSide = 0; 
    } 

    private bool IsFull() 
    { 
     return _tail == _head && _tailMirrorSide != _headMirrorSide; 
    } 

    private bool IsEmpty() 
    { 
     return _tail == _head && _tailMirrorSide == _headMirrorSide; 
    } 

    private void MovePointer(ref int pointer, ref byte mirrorSide) 
    { 
     pointer = pointer + 1; 

     if (pointer == _size) 
     { 
      mirrorSide ^= 1; 
      pointer = 0; 
     } 
    } 

    public void Write(T obj) 
    { 
     lock (_lock) 
     { 
      _buffer[_head] = obj; 

      if (IsFull()) 
      { 
       MovePointer(ref _tail, ref _tailMirrorSide); 
      } 
      MovePointer(ref _head, ref _headMirrorSide); 
     } 
    } 

    public T Read() 
    { 
     lock (_lock) 
     { 
      if (IsEmpty()) 
      { 
       return null; 
      } 

      var obj = _buffer[_tail]; 
      MovePointer(ref _tail, ref _tailMirrorSide); 

      return obj; 
     } 
    } 
} 
+1

製作一個循環緩衝區線程安全和等待釋放是一個簡單的練習1個生產者和1名消費者。但是在這種情況下你的代碼甚至不安全。支持多個作家更復雜。 –

+0

重點關注你真正需要什麼,以及爲什麼不是ConcurrentQueue。 –

+0

我需要一個有限的FIFO列表,其功能是如果達到限制,「舊」值將被覆蓋。環形緩衝區不應該滿足這些要求嗎? – mckn

回答

1

至於你接觸來自不同線程的數據,您必須將同步訪問。最簡單的方法是lock()指令,它應該放在Read()和Write()方法中。

顯然,寫()應該有鎖(),以避免併發提交到同一個存儲單元(因爲緩衝區是從每一個作家積累的數據,而不是「勝利者」只)。

閱讀()應具有鎖(),以及因爲它

  • 修改相同的內部成員寫()不
  • 可以通過編譯器的優化以及運行時間指令重新排序的影響(因爲計算效果相同,但是線程間交互可能會被破壞)

PS在先進水平,可以使用單向記憶障礙,而不是單向鎖,但這需要大量的經驗

1

如果您有多個線程訪問緩衝區,並且它們都只能讀取,那麼您不必使用鎖定。但是,如果一個或多個線程正在修改數據,那麼您將需要一個鎖。所以你的情況,答案是明確的,明確的YES,特別是因爲你的Read()方法也改變數據:緩衝區指針的位置。

+0

實際上,對於「我必須爲寫入函數添加鎖嗎?」這個問題,答案是「否」因爲他顯然也需要一些東西來閱讀;) – Voo