假设我有可以读写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
在读取之前不会写两次。我使用锁定会阻止吗?
此类操作会被称为“事务IO”吗?
如果这确实是正确的,还有其他更有效的方法来执行相同类型的操作吗?
修改
我理解为什么锁定Singleton.Instance
可能会很糟糕,如果要锁定Singleton.Instance
,然后调用Singleton.Instance
中也试图锁定自身的方法,那么陷入僵局。
我原计划在单例中使用私有对象来锁定。但是由于下面概述的情况,我有点自言自语。其中,我不确定这是否正确。
(在Thread1和Thread2上运行上面两种方法(减少锁定)
Write
,Singleton.Instance
锁定Write
,但被锁Singleton.Instance
完成Write
并释放锁Write
执行,Singleton.Instance
锁定Read
,但被锁Singleton.Instance
完成Write
并释放锁Read
执行,Singleton.Instance
锁定Read
,但被锁Singleton.Instance
完成Read
并释放锁Read
,Singleton.Instance
锁定Singleton.Instance
完成Read
并释放锁在这种情况下,连续的串行端口有两个Writes
是不合适的。我需要能够为某些类型的通信背靠背地进行Write
Read
。
答案 0 :(得分:2)
对于锁定对象,我会使用same reasoning on why not to lock(this) ever在类(即非静态)而不是单例实例本身上使用私有字段。
我通常使用类似这样的声明,因为声明锁定对象作为自编代码更具可读性。
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;
}
}
}
现在,任何调用线程都可以以线程安全的方式调用这些方法。
答案 1 :(得分:0)
您永远不应该锁定公共对象(在代码的使用者可以访问的对象上,或者在实例化它们的类型之外)。您始终应该只依赖于私有对象的锁定,这样您就可以确保没有人会发生意外锁定并陷入死锁状态。否则您的实现没问题,您必须只允许一个线程访问您的端口。
由于您的单例中已经有一个私有对象,而且是SerialPort实例,请重新设计您的类,如下例所示:
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance { get { return lazy.Value; } }
private SerialPort commPort = new SerialPort();
private Singleton()
{
// Setup SerialPort
}
public String Read()
{
lock (commPort)
return commPort.ReadLine();
}
public void Write(String cmd)
{
lock (commPort)
commPort.WriteLine(cmd);
}
}
从SerialPort文档中,我们可以推断出写和读是非线程安全的:http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.aspx。所以,是的,您必须将您的R / W同步到SerialPorts。
答案 2 :(得分:0)
我使用锁定会阻止吗?
是的,因为一次只能有一个线程在这两个方法中执行。这是安全的,我认为没有问题。考虑锁定使用new object()
创建的私有对象。这有点安全,因为它可以防止某些错误。
此类操作会被称为“事务IO”吗?
这个词并不为人所知。无论这意味着什么,这不是“交易IO”。