2009-12-07 44 views
2

我有一個繼承自MemoryStream的類以提供一些緩衝。該類的工作原理與預期的完全一致,但每隔一段時間,在讀取期間出現InvalidOperationException,並顯示錯誤消息:可以鎖定列表失敗

收集已被修改;枚舉操作可能不會執行。

我的代碼如下,並且列舉了集合的唯一路線似乎是:

m_buffer = m_buffer.Skip(count).ToList(); 

不過,我有一個和所有可以鎖定,所以我內修改m_buffer對象等操作對於Write操作如何幹擾Read來導致異常感到困惑?

public class MyMemoryStream : MemoryStream 
{ 
    private ManualResetEvent m_dataReady = new ManualResetEvent(false); 
    private List<byte> m_buffer = new List<byte>(); 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
     lock (m_buffer) 
     { 
      m_buffer.AddRange(buffer.ToList().Skip(offset).Take(count)); 
     } 
     m_dataReady.Set(); 
    } 

    public override int Read(byte[] buffer, int offset, int count) 
    { 
     if (m_buffer.Count == 0) 
     { 
      // Block until the stream has some more data. 
      m_dataReady.Reset(); 
      m_dataReady.WaitOne(); 
     } 

     lock (m_buffer) 
     { 
      if (m_buffer.Count >= count) 
      { 
       // More bytes available than were requested. 
       Array.Copy(m_buffer.ToArray(), 0, buffer, offset, count); 
       m_buffer = m_buffer.Skip(count).ToList(); 
       return count; 
      } 
      else 
      { 
       int length = m_buffer.Count; 
       Array.Copy(m_buffer.ToArray(), 0, buffer, offset, length); 
       m_buffer.Clear(); 
       return length; 
      } 
     } 
    } 
} 

回答

5

我不能說究竟發生了什麼,從您發佈的代碼去錯了,但有點古怪的是,你鎖定m_buffer,但更換的緩衝,鎖定的收集並不總是在集合正在閱讀和修改。

它是使用專用的私有隻讀對象鎖定良好做法:

private readonly object locker = new object(); 

    // ... 
    lock(locker) 
    { 
     // ... 
    } 
3

你已經有至少一個數據的比賽:在Read方法,如果你是if(m_buffer.Count == 0)經過前期捷足先登,塊之前和lock,Count可以再次爲0。您應該檢查lock內計數,並使用Monitor.WaitMonitor.Pulse和/或Monitor.PulseAll的等待/信號的協調,如:

// On Write 
lock(m_buffer) 
{ 
    // ... 
    Monitor.PulseAll(); 
} 

// On Read 
lock(m_buffer) 
{ 
    while(m_buffer.Count == 0) 
     Monitor.Wait(m_buffer); 
// ... 

你必須保護所有訪問m_buffer,並調用m_buffer.Count在這方面並不特別。

+0

這確實是一個錯誤,但它不應該導致InvalidOperationException。 – SoftMemes 2009-12-07 09:44:13

+0

是的,它不會。這就是爲什麼我說「至少有一個」。你在回答中提到的問題很可能導致這個問題。 – 2009-12-07 09:49:24

+0

如果我在持有鎖的Read方法中等待,我將如何獲得Write方法中的鎖? – sipwiz 2009-12-07 09:54:56

0

是否在另一個線程的某處修改buffer的內容,我懷疑這可能是給出錯誤的枚舉而不是m_buffer