在c#中同步缓冲区
本文关键字:缓冲区 同步 | 更新日期: 2023-09-27 18:18:03
所以我是c#和多线程工作的新手。我得到了这个应用程序,它有2个类,作家和读者,两者都有一个引用到我的CharacterBuffer类。所以我向程序输入一个字符串,写入器应该把字符读入缓冲区,读取器应该从缓冲区中读入,所有这些都是同步的。这是一个缓冲类:
public class CharacterBuffer
{
private static object Lock = new object();
private Queue result;
public CharacterBuffer()
{
result = new Queue();
lock (result);
}
public void addChar(char c)
{
result.Enqueue(c);
Monitor.PulseAll(result);
}
public char readChar()
{
Monitor.Wait(result);
return (char) result.Dequeue();
}
}
所以我认为我要做的是首先声明队列并给它加一个锁。因此,当读取器调用readChar()时,它将不得不等待,直到写入器调用addChar(char c)。但每当我启动读取器和写入器线程时,它立即抛出异常'System.Threading.SynchronizationLockException' occurred in mscorlib.dll
显然我做错了什么。我已经寻找了一个答案,似乎调用监视器的代码块必须在同一块中进行所有调用。因为在这种情况下,我单独的线程调用它,而不是Buffer本身。问题是;那么该怎么做呢?
所以问题是:该怎么做呢?
使用管道!
var buffer = new BlockingCollection<string>();
// Start reading thread
var readTask = Task.Run(() =>
{
try
{
// Read data from source and put to buffer
foreach (var data in source)
{
buffer.Add(data);
}
}
finally
{
// Signal the end of the data
buffer.CompleteAdding();
}
});
// Start writing thread
var writeTask = Task.Run(() =>
{
foreach (string data in buffer.GetConsumingEnumerable())
{
// Process data
}
});
Task.WaitAll(readTask, writeTask);
不需要手动锁定。只需使用简化编程和防止错误的现代方法。
如果您担心在多个线程中修改队列,请使用以下代码:
public class CharacterBuffer
{
private object padlock = new object();
private Queue result = new Queue();
public void AddChar(char c)
{
// lock the padlock so that no two threads try to read/write at the same time
lock (padlock)
{
result.Enqueue(c);
}
}
public char ReadChar()
{
// lock the padlock so that no two threads try to read/write at the same time
lock (padlock)
{
return (char) result.Dequeue();
}
}
}
如果你想实现阻塞队列,并做信号,以便读取器等待写入器将东西放入队列,使用以下代码:
public class CharacterBuffer
{
private object padlock = new object();
private Queue result = new Queue();
public void AddChar(char c)
{
// lock the padlock so that no two threads try to read/write at the same time
lock (padlock)
{
result.Enqueue(c);
if (result.Count == 1)
{
// wake up any blocked dequeue
Monitor.PulseAll(padlock);
}
}
}
public char ReadChar()
{
// lock the padlock so that no two threads tries to read/write at the same time
lock (padlock)
{
// block the thread and wait until there is something in the queue
while (result.Count == 0)
{
Monitor.Wait(padlock);
}
return (char) result.Dequeue();
}
}
}
可以像这样简单地锁定:
public class CharacterBuffer
{
private static object Lock = new object();
private Queue result;
public CharacterBuffer()
{
result = new Queue();
}
public void addChar(char c)
{
lock(Lock)
{
result.Enqueue(c);
Monitor.PulseAll(result);
}
}
public char readChar()
{
lock(Lock)
{
return (char) result.Dequeue();
}
}
}
锁是一个语法糖,它在{部分进入关键会话,并将其留在}中(或者如果代码以某种方式退出块)。
您正在以这种方式实现阻塞队列。在构造函数中,你锁定了队列本身,这是错误的。锁定应该在加入队列或退出队列(放入字符或删除字符)以及监视时执行。我会做类似的事情:
public class CharacterBuffer
{
private static object Lock = new object();
private Queue result;
public CharacterBuffer()
{
result = new Queue();
//lock (result); <-- this is WRONG
}
public void addChar(char c)
{
Monitor.Enter(Lock){
result.Enqueue(c);
}
Monitor.Pulse(Lock);
}
public char readChar()
{
char c;
Monitor.Wait(result);
Monitor.Enter(Lock){
c = (char)result.Deqeueue();
}
return c;
}
}
请注意此代码未修改。它会给你一个提示