2009-12-07 52 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