为线程安全设备 IO 锁定单例 class?
Locking on a singleton class for thread safe device IO?
假设我有单例 class 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
.
中读取
我想确保当一个线程正在读然后写时它不会被另一个线程中断。这样做的方法是在 Singleton.Instance
本身上 lock
吗?
// 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
在它被调用之前没有写太多次读。我使用锁定会阻止这种情况吗?
这种行为会被称为"transactional IO"吗?
如果这确实是正确的,是否还有其他更有效的方法来执行相同类型的操作?
编辑:
我理解为什么锁定 Singleton.Instance
可能是不好的,如果某些东西锁定 Singleton.Instance
然后调用 Singleton.Instance
中的方法也试图锁定自己,会有一个僵局。
本来打算用单例中的私有对象来加锁。但由于下面概述的情况,我有点说服自己放弃了。其中,我不确定这是否正确。
(在 Thread1 和 Thread2 上使用上述 运行 的两种方法(减少锁定))
- 线程 1 调用
Write
、Singleton.Instance
锁
- 线程2调用
Write
,但被锁阻塞
Singleton.Instance
完成Write
并释放锁
- Thread2 调用
Write
执行,Singleton.Instance
锁定
- Thread1 调用
Read
,但被锁阻塞
Singleton.Instance
完成Write
并释放锁
- Thread1s
Read
执行,Singleton.Instance
锁定
- 线程2调用
Read
,但被锁阻塞
Singleton.Instance
完成Read
并释放锁
- Thread2s
Read
被执行,Singleton.Instance
锁
Singleton.Instance
完成Read
并释放锁
这种情况下连续有两个Writes
到串口是不合适的。对于某些类型的通信,我需要能够进行 Write
Read
背靠背。
您永远不应该锁定 public 个对象(您的代码的使用者或您自己在实例化它们的类型之外可以访问的对象)。您应该始终只依赖于对私有对象的锁定,以确保没有人会进行意外锁定并陷入死锁情况。否则你的实现没问题,你必须只允许一个线程访问你的端口。
由于您的单例中已经有一个私有对象,即 SerialPort 实例,请重新设计您的 class,如下面的示例:
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。
Does my use of locking prevent that?
是的,因为在这两个方法内部一次只能执行一个线程。这是安全的,我认为这没有问题。考虑锁定使用 new object()
创建的私有对象。这样更安全一些,因为它可以防止某些错误。
Would this type of action be called "transactional IO"?
这个词并不为人所知。不管是什么意思,这都不是 "transactional IO".
对于锁定对象,我会在 class(即非静态)上使用私有 字段,而不是使用 same reasoning on why not to lock(this) ever.
我通常使用这样的声明,因为声明锁对象作为自文档化代码更具可读性。
private readonly object _LEDLock = new object();
这样别人去看的时候会说"Oh, this is the lock object that guards thread access to the LED resource."
恕我直言,我认为 SetLEDStatus
和 IsLEDOn
方法(带锁定)中的行为最好封装在您的 Singleton
class 中,如下所示:
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;
}
}
}
现在,任何调用线程都可以以线程安全的方式调用这些方法。
假设我有单例 class 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
.
我想确保当一个线程正在读然后写时它不会被另一个线程中断。这样做的方法是在 Singleton.Instance
本身上 lock
吗?
// 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
在它被调用之前没有写太多次读。我使用锁定会阻止这种情况吗?
这种行为会被称为"transactional IO"吗?
如果这确实是正确的,是否还有其他更有效的方法来执行相同类型的操作?
编辑:
我理解为什么锁定 Singleton.Instance
可能是不好的,如果某些东西锁定 Singleton.Instance
然后调用 Singleton.Instance
中的方法也试图锁定自己,会有一个僵局。
本来打算用单例中的私有对象来加锁。但由于下面概述的情况,我有点说服自己放弃了。其中,我不确定这是否正确。
(在 Thread1 和 Thread2 上使用上述 运行 的两种方法(减少锁定))
- 线程 1 调用
Write
、Singleton.Instance
锁 - 线程2调用
Write
,但被锁阻塞 Singleton.Instance
完成Write
并释放锁- Thread2 调用
Write
执行,Singleton.Instance
锁定 - Thread1 调用
Read
,但被锁阻塞 Singleton.Instance
完成Write
并释放锁- Thread1s
Read
执行,Singleton.Instance
锁定 - 线程2调用
Read
,但被锁阻塞 Singleton.Instance
完成Read
并释放锁- Thread2s
Read
被执行,Singleton.Instance
锁 Singleton.Instance
完成Read
并释放锁
这种情况下连续有两个Writes
到串口是不合适的。对于某些类型的通信,我需要能够进行 Write
Read
背靠背。
您永远不应该锁定 public 个对象(您的代码的使用者或您自己在实例化它们的类型之外可以访问的对象)。您应该始终只依赖于对私有对象的锁定,以确保没有人会进行意外锁定并陷入死锁情况。否则你的实现没问题,你必须只允许一个线程访问你的端口。
由于您的单例中已经有一个私有对象,即 SerialPort 实例,请重新设计您的 class,如下面的示例:
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。
Does my use of locking prevent that?
是的,因为在这两个方法内部一次只能执行一个线程。这是安全的,我认为这没有问题。考虑锁定使用 new object()
创建的私有对象。这样更安全一些,因为它可以防止某些错误。
Would this type of action be called "transactional IO"?
这个词并不为人所知。不管是什么意思,这都不是 "transactional IO".
对于锁定对象,我会在 class(即非静态)上使用私有 字段,而不是使用 same reasoning on why not to lock(this) ever.
我通常使用这样的声明,因为声明锁对象作为自文档化代码更具可读性。
private readonly object _LEDLock = new object();
这样别人去看的时候会说"Oh, this is the lock object that guards thread access to the LED resource."
恕我直言,我认为 SetLEDStatus
和 IsLEDOn
方法(带锁定)中的行为最好封装在您的 Singleton
class 中,如下所示:
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;
}
}
}
现在,任何调用线程都可以以线程安全的方式调用这些方法。