Скажем, у меня есть одноэлементный класс Singleton
, который может читать и писать в SerialPort
.Блокировка на одноэлементном классе для потокобезопасного устройства IO?
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»?
Если это действительно так, есть ли другие более эффективные способы выполнения такого же действия?
EDIT:
Я понимаю, почему замок на Singleton.Instance
может быть плохим, если что-то зафиксировать на Singleton.Instance
, а затем вызвать метод в Singleton.Instance
, который также пытается заблокировать на себя, было бы тупиковым ,
Первоначально планировалось использовать закрытый объект в одиночном режиме для блокировки. Но я как бы говорил из этого из-за ситуации, описанной ниже. Который, я не уверен, что это правильно.
(с использованием двух методов (MINUES запирающий) выше, работающие на thread1 и thread2)
- Резьба1 называет
Write
,Singleton.Instance
замки - Резьба2 называет
Write
, но заблокирован замком Singleton.Instance
завершаетWrite
и освобождает замок- Выполняется вызов Thread2s для
Write
,Singleton.Instance
замки - Резьба1 называет
Read
, но блокируется замком Singleton.Instance
завершаетWrite
и освобождает замок- Thread1s
Read
исполняет,Singleton.Instance
замки - Резьба2 называет
Read
, но блокируется замком Singleton.Instance
завершаетсяRead
и освобождает замок- Thread2s
Read
выполнен,Singleton.Instance
замки Singleton.Instance
завершаетRead
и снимает блокировку
В этом случае есть два Writes
к последовательному порту в ряде, который является неправомерным. Мне нужно иметь возможность делать Write
Read
спиной к спине для некоторых видов связи.
См. Мое редактирование на вопрос о том, как использовать закрытый объект для блокировки. – KDecker
Я думаю, что это ничего не меняет. Закрытое поле точно функционально эквивалентно самому экземпляру блокировки. Не используя сам экземпляр, вы не только избегаете себя создателем тупика, как вы упомянули, но и избегаете других. Что касается особенностей порядка чтения и записи, то это просто: все, что вам нужно выполнить последовательно, находится внутри одного оператора блокировки. Если ваш вариант использования таков, что это невозможно, что не похоже на него, то вы смотрите на более крупный рабочий процесс управления асинхронизацией. – ibgib
Кроме того, я бы обязательно инициализировал ваш синглтон до того, как возникнет многопоточность, иначе вы могли бы создать два потока для создания самого ресурса. То есть не имеют первого вызова из (фонового) потока1 или потока2. – ibgib