说我有一个单例类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;
}
}
在这种情况下,如果
SetLEDStatus
和IsLEDOn
几乎同时被调用,则我想确保在读取SerialPort
之前,不要将其写入过多。我使用锁定可以防止这种情况吗?这种类型的 Action 会被称为“交易IO”吗?
如果确实正确,那么还有其他更有效的方法来执行相同类型的操作吗?
编辑:
我知道为什么锁定
Singleton.Instance
可能会很糟糕,如果要锁定Singleton.Instance
,然后在Singleton.Instance
中调用一个也试图锁定自身的方法,则会出现死锁。我最初计划在单例中使用私有(private)对象进行锁定。但是由于下面概述的情况,我有点不喜欢它。我不确定这是否正确。
(使用上面在线程1和线程2上运行的两种方法(减少锁定))
Write
,Singleton.Instance
锁定Write
,但被锁Singleton.Instance
完成Write
并释放锁Write
的Thread2s调用,Singleton.Instance
锁定Read
,但被锁Singleton.Instance
完成Write
并释放锁Read
执行,Singleton.Instance
锁定Read
,但被锁Singleton.Instance
完成Read
并释放锁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资源的访问。”
恕我直言,我认为更好地将
SetLEDStatus
和IsLEDOn
方法中的行为(带有锁定)封装在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/