C# Monitor.Enter 同步锁异常

C# Monitor.Enter SynchronizationLockException

我正在开发一个 C# 4.5 应用程序,其中许多线程需要访问同一个串行端口。

因为我也会在串行端口上接收响应,所以我想避免进一步写入,直到我收到响应并阅读它。

我有一个 SerialHandler class,它是 SerialPort class 的包装器,写入和读取方法如下所示:

public void write(string message)
{
  Monitor.Enter(lockingObject);
  //Stuff
  serialPort.write(message);
}

public string read()
{
  //Reading procedure
  Monitor.Exit(lockingObject);
}

但是一旦我从表单发出写入,我就会收到 SynchronizationLockException 说 "Object synchronization method was called from an unsynchronized block of code"。 lockingObject 是私有且只读的对象变量。

我怎样才能避免这个错误? 非常感谢

编辑,它应该是这样工作的:
线程 A 可以访问 read/write
线程 B 尝试获取访问权限但失败
线程 A 开始读取,所以他释放了他的锁
线程 B 现在可以执行写入、获取他的锁、读取并移除他的锁

你可以使用 Mutex

class SerialHandler 
{
    private Mutex _mutex = new Mutex();
    private SerialPort _serialPort;
    public void write(string message)
    {
      _mutex.WaitOne();
      //Stuff
      _serialPort.write(message);
    }

    public string read()
    {
      //Reading procedure
      _mutex.ReleaseMutex();
    }
}

该问题表示您已尝试释放未获取的锁。

异常抛出是因为不同的线程是trying to release the object.

这是解决您的问题的简单方法:

class MySerialPort
{
    private AutoResetEvent _waithandle = new AutoResetEvent(true);
    private SerialPort _serialPort;

    public void write(string message)
    {
        _waithandle.WaitOne();
        //Stuff
        _serialPort.Write(message);
    }

    public string read()
    {
        try
        {
           ...
           ...
        }
        finally
        {
            _waithandle.Set();
        }
    }

}

第一个线程将写入,​​然后任何新的写入操作都将被阻塞,直到读取释放其中一个。

编辑

我在下面的评论中看到您正在寻找最快的解决方案。 根据这个 link,下面的代码将在 60ns(在 Intel Core i7 860 上)完成工作,这要好得多

class MySerialPort
{
    private ManualResetEventSlim _waithandle = new ManualResetEventSlim(true);
    private SerialPort _serialPort;
    private readonly object _sync = new object();

    public void write(string message)
    {
        lock (_sync)
        {
            _waithandle.Wait();
            //Stuff
            _serialPort.Write(message);
            _waithandle.Reset();
        }

    }

    public string read()
    {
        try
        {
         ....
        }
        finally
        {
            _waithandle.Set();
        }
    }
}