Насколько критична блокировка - степень и способы отказа
Я стресс-тестировал приведенный ниже код, и он, кажется, работает нормально - каковы опасности не блокирования в простом случае, когда есть запись одного потока и отдельный поток чтения простой переменной?
Есть класс, который имеет публичную собственность...
Public Class PropHolder
Private _myVar As Double
Public Property myVar() As Double
Get
Return _myVar
End Get
Set(ByVal value As Double)
_myVar = value
End Set
End Property
End Class
... и другой, который использует экземпляр этого класса
Public Class Form1
Public _propHolder As New PropHolder
Delegate Sub dlgPostVar()
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
While Not BackgroundWorker1.CancellationPending
_propHolder.myVar = someValue 'worker thread writing var
End While
End Sub
Private Sub BackgroundWorker2_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker2.DoWork
While Not BackgroundWorker2.CancellationPending
Me.Invoke(New dlgPostVar(AddressOf postVal)) 'invoke UI thread to write var
End While
End Sub
Private Sub postVal()
RichTextBox1.AppendText(_propHolder.myVar.ToString & vbCrLf)
End Sub
End Class
В приведенном выше примере myVar постоянно меняется и записывается в RichTextBox так быстро, как только пользовательский интерфейс может идти в ногу. Не все значения фиксируются, но это не важно - давайте представим, что RichTextBox заботится только о выборке текущего значения myVar на досуге.
Все, что я прочитал, наводит меня на мысль, что это небезопасно, но мне еще предстоит заставить его потерпеть неудачу, даже когда две переменные читают и пишут (один читатель, один писатель) эту переменную непрерывно. Что может пойти не так и почему?
Изменить: чтобы быть более конкретным, этот вопрос является скорее гипотетическим, чем практическим. Я знаю, что эта ситуация требует блокировки "по книге", вопрос состоит в том, чтобы исследовать широту потенциальных последствий отсутствия блокировки в этом случае, возможно, в виде полного сбора всех возможных способов отказа.
Одним из примеров таким образом является чтение из переменной, записываемой в:
- Тема 1 -> Читает младшее слово из памяти
- Thread 2 -> CPU перемещает младшее слово нового значения в память
- Thread 2 -> CPU перемещает старшее слово нового значения в память
- Тема 1 -> Читает старшее слово из памяти
Значение потока 1 теперь (HighWord[новое])(LowWord[старое]) ** повреждено
Ошибка программы ограничена повреждением, вызванным обработкой этого поврежденного значения. Другие последствия этого режима отказа? Есть ли?
Дальнейшие примеры? Книги / ссылки приветствуются.
Правка № 2: Мое намерение здесь состоит в том, чтобы изучить в среде.NET степень и форму непредсказуемости и ошибок, которые свойственны платформе и конкретно не связаны с последствиями того, как любая конкретная программа может впоследствии потерпеть неудачу перед лицом такая ошибка.
В приведенном выше примере я ничего не делаю со значением, кроме записи его в виде текста. Если я получаю плохое значение, я записываю плохое значение в текст - ничего не взрывается, программа не падает, империи не падают, а орбита Меркурия остается стабильной.
3 ответа
В этом случае вам нужна блокировка, так как переменная пишет в double
не атомарны. Поэтому это возможно сломать, просто крайне маловероятно.
Как указал Джейсон, тебе нужен замок. Вы, вероятно, еще не видели этого, потому что, как отмечали другие, проблемы с потоками общеизвестно трудно воспроизвести. Для запуска этого неудачного сценария может потребоваться очень специфический набор обстоятельств (включая пользовательскую нагрузку, другие процессы, объем доступной памяти, количество одновременно работающих потоков в вашем приложении и т. Д.).
Идея должна быть довольно простой и прямой. Если переменная или другой ресурс будет затронут отдельным потоком выполнения, заблокируйте его перед этим. Это гарантирует, что запрашивающий поток будет иметь единственный доступ к переменной или ресурсу до тех пор, пока не завершит работу с ним, что исключает вероятность того, что вы испортите данные или войдете в какое-то другое неприятное состояние, приводящее вашу машину в тупик.
Есть веская причина, по которой у каждого класса в MSDN есть документация по безопасности потоков. Microsoft относится к этому серьезно, как и вы.
То, что вы тестируете, не вызывает проблем, не означает, что этого не произойдет. Проблемы с потоками труднее всего отлаживать. Причиной блокировки кода является предотвращение устаревания информации или двойная обработка. Возможны следующие проблемы: один поток устанавливает значение, а затем другой переопределяет его, не позволяя обработать его. В вашем случае вы можете столкнуться с проблемой, когда она в данный момент записывает в переменную, поскольку другой поток читает из нее, что вызывает проблему.
Из MSDN:
"Все члены этого типа являются потокобезопасными. Члены, которые, по-видимому, изменяют состояние экземпляра, фактически возвращают новый экземпляр, инициализированный новым значением. Как и в случае любого другого типа, чтение и запись в общую переменную, которая содержит экземпляр этого типа, должны быть защищен замком для гарантии безопасности резьбы.
предосторожность
Назначение экземпляра этого типа не является потокобезопасным на всех аппаратных платформах, поскольку двоичное представление этого экземпляра может быть слишком большим для назначения в одной атомарной операции.
"