在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本身。问题是;那么该怎么做呢?

在c#中同步缓冲区

所以问题是:该怎么做呢?

使用管道!

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;
    }
}

注意此代码未修改。它会给你一个提示