C# 锁定列表会失败吗

C# 锁定列表会失败吗,c#,locking,C#,Locking,我有一个从MemoryStream继承的类,以便提供一些缓冲。该类完全按照预期工作,但在读取过程中,我不时会收到一个InvalidOperationException,错误消息为 收集被修改;枚举操作不能执行 下面是我的代码,枚举集合的唯一一行似乎是: m_buffer = m_buffer.Skip(count).ToList(); 但是,我有这个操作和所有其他可以修改锁中的m_buffer对象的操作,所以我对写操作如何干扰读操作从而导致该异常感到困惑 public class MyMemo

我有一个从MemoryStream继承的类,以便提供一些缓冲。该类完全按照预期工作,但在读取过程中,我不时会收到一个InvalidOperationException,错误消息为

收集被修改;枚举操作不能执行

下面是我的代码,枚举集合的唯一一行似乎是:

m_buffer = m_buffer.Skip(count).ToList();
但是,我有这个操作和所有其他可以修改锁中的m_buffer对象的操作,所以我对写操作如何干扰读操作从而导致该异常感到困惑

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;
            }
        }
    }
}
公共类MyMemoryStream:MemoryStream
{
private ManualResetEvent m_dataReady=新的ManualResetEvent(错误);
私有列表m_buffer=新列表();
公共重写无效写入(字节[]缓冲区、整数偏移量、整数计数)
{
锁(m_缓冲区)
{
m_buffer.AddRange(buffer.ToList().Skip(offset.Take(count));
}
m_dataReady.Set();
}
公共重写整型读取(字节[]缓冲区、整型偏移量、整型计数)
{
如果(m_buffer.Count==0)
{
//阻塞,直到流具有更多数据。
m_dataReady.Reset();
m_dataReady.WaitOne();
}
锁(m_缓冲区)
{
如果(m_buffer.Count>=计数)
{
//可用字节数超过请求的字节数。
Copy(m_buffer.ToArray(),0,buffer,offset,count);
m_buffer=m_buffer.Skip(count.ToList();
返回计数;
}
其他的
{
int length=m_buffer.Count;
Copy(m_buffer.ToArray(),0,buffer,offset,length);
m_buffer.Clear();
返回长度;
}
}
}
}

我无法从您发布的代码中确切地说出哪里出了问题,但有一点奇怪的是,您锁定了m_缓冲区,但替换了缓冲区,因此锁定的集合并不总是正在读取和修改的集合

最好使用专用的私有只读对象进行锁定:

private readonly object locker = new object();

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

这里至少有一个数据争用:在
Read
方法上,如果在
if(m_buffer.Count==0)
块之后和
锁之前被抢占,
Count
可以再次为0。您应该检查
锁内的计数,并使用和/或进行等待/信号协调,如下所示:

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

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

您必须保护对
m\u buffer
的所有访问,并且调用
m\u buffer.Count
在这方面并不特别。

您是否在另一个线程的某处修改了
buffer
的内容,我怀疑可能是枚举给出了错误,而不是
m\u buffer
,这确实是一个错误,但这不应该导致无效手术例外。是的,不会的。这就是为什么我说“至少一个”。您在回答中提到的最有可能导致该问题。如果我在持有锁的读取方法中等待,我如何才能在写入方法中获取锁?Monitor.Wait在阻塞之前释放锁,并在返回之前重新获取它。阅读MSDN页面。Monitor.Wait在等待脉冲时释放锁。您必须再次检查计数的原因是,在退出等待调用时,无法确保在重新获取锁之前没有运行另一个线程。