C# 锁定列表会失败吗
我有一个从MemoryStream继承的类,以便提供一些缓冲。该类完全按照预期工作,但在读取过程中,我不时会收到一个InvalidOperationException,错误消息为 收集被修改;枚举操作不能执行 下面是我的代码,枚举集合的唯一一行似乎是:C# 锁定列表会失败吗,c#,locking,C#,Locking,我有一个从MemoryStream继承的类,以便提供一些缓冲。该类完全按照预期工作,但在读取过程中,我不时会收到一个InvalidOperationException,错误消息为 收集被修改;枚举操作不能执行 下面是我的代码,枚举集合的唯一一行似乎是: m_buffer = m_buffer.Skip(count).ToList(); 但是,我有这个操作和所有其他可以修改锁中的m_buffer对象的操作,所以我对写操作如何干扰读操作从而导致该异常感到困惑 public class MyMemo
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在等待脉冲时释放锁。您必须再次检查计数的原因是,在退出等待调用时,无法确保在重新获取锁之前没有运行另一个线程。