说我有一个单例类Singleton,它可以读写SerialPort

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    SerialPort commPort = new SerialPort();

    private Singleton()
    {
        // Setup SerialPort
    }

    public String Read()
    {
        return commPort.ReadLine();
    }

    public void Write(String cmd)
    {
        commPort.WriteLine(cmd);
    }
}

现在再说一遍,我有多个线程在SerialPort的末尾访问设备。有些线程可能只写SerialPort,有些可能写然后从SerialPort读取。

我想确保当一个线程正在读然后写时,它不会被另一个线程打断。这样做的方法是在lock本身上的Singleton.Instance吗?
// Running on thread 1
public Boolean SetLEDStatus(int onOff)
{
    lock(Singleton.Instance)
    {
        Singleton.Instance.Write("SET LED " + onOff.ToString() + "\r\n");
        String status = Singleton.Instance.ReadLine();
        return (status.Contains("SUCCESS")) ? true : false;
    }
}

// Running on thread 2
public Boolean IsLEDOn()
{
    lock(Singleton.Instance)
    {
        Singleton.Instance.Write("GET LED\r\n");
        return (Singleton.Instance.ReadLine().Contains("ON")) ? true : false;
    }
}

在这种情况下,如果SetLEDStatusIsLEDOn几乎同时被调用,则我想确保在读取SerialPort之前,不要将其写入过多。我使用锁定可以防止这种情况吗?

这种类型的 Action 会被称为“交易IO”吗?

如果确实正确,那么还有其他更有效的方法来执行相同类型的操作吗?

编辑:

我知道为什么锁定Singleton.Instance可能会很糟糕,如果要锁定Singleton.Instance,然后在Singleton.Instance中调用一个也试图锁定自身的方法,则会出现死锁。

我最初计划在单例中使用私有(private)对象进行锁定。但是由于下面概述的情况,我有点不喜欢它。我不确定这是否正确。

(使用上面在线程1和线程2上运行的两种方法(减少锁定))
  • Thread1调用WriteSingleton.Instance锁定
  • Thread2调用Write,但被锁
  • 阻止
  • Singleton.Instance完成Write并释放锁
  • 执行对Write的Thread2s调用,Singleton.Instance锁定
  • Thread1调用Read,但被锁
  • 阻止
  • Singleton.Instance完成Write并释放锁
  • Thread1s Read执行,Singleton.Instance锁定
  • Thread2调用Read,但被锁
  • 阻止
  • Singleton.Instance完成Read并释放锁
  • Thread2s Read被执行,Singleton.Instance锁定
  • Singleton.Instance完成Read并释放锁

  • 在这种情况下,串行端口中有两个不正确的Writes。对于某些类型的通信,我需要能够背对背做一个Write Read

    最佳答案

    对于锁对象,我将使用same reasoning on why not to lock(this) ever在类(即非静态)上使用私有(private)字段而不是单例实例本身。

    我通常使用如下所示的声明,因为声明锁对象作为自记录代码更具可读性。

    private readonly object _LEDLock = new object();
    

    这样,当其他人看时,他们说:“哦,这是锁定对象,可保护线程对LED资源的访问。”

    恕我直言,我认为更好地将SetLEDStatusIsLEDOn方法中的行为(带有锁定)封装在Singleton类中,如下所示:
    public sealed class Singleton
    {
        private static readonly Lazy<Singleton> lazy =
            new Lazy<Singleton>(() => new Singleton());
    
        public static Singleton Instance { get { return lazy.Value; } }
    
        SerialPort commPort = new SerialPort();
    
        private readonly object _LEDLock = new object();
    
        private Singleton()
        {
            // Setup SerialPort
        }
    
        /// <summary>
        /// This goes in the singleton class, because this is the class actually doing the work.
        /// The behavior belongs in this class. Now it can be called with thread-safety from
        /// any number of threads
        /// </summary>
        public Boolean SetLEDStatus(int onOff)
        {
            lock(_LEDLock)
            {
                var cmd = "SET LED " + onOff.ToString() + "\r\n";
                commPort.WriteLine(cmd);
                string status = commPort.ReadLine();
                return (status.Contains("SUCCESS")) ? true : false;
            }
        }
    
        public Boolean IsLEDOn()
        {
            lock(_LEDLock)
            {
                commPort.Write("GET LED\r\n");
                var result = commPort.ReadLine().Contains("ON")) ? true : false;
                return result;
            }
        }
    }
    

    现在,任何调用线程都可以以线程安全的方式调用这些方法。

    关于c# - 锁定线程安全设备IO的单例类?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31731593/

    10-13 06:23