Решение проблемы: синхронизация
В предыдущей программе возникает ситуация, когда результат работы программы зависит от порядка выполнения потоков. Чтобы избавиться от нее, необходимо убедиться в том, что команды типа
If mHouse.HouseTemp < mHouse.MAX_TEMP - 5 Then...
полностью отрабатываются активным потоком до того, как он будет прерван. Это свойство называется атомарностыд — блок кода должен выполняться каждым потоком без прерывания, как атомарная единица. Группа команд, объединенных в атомарный блок, не может быть прервана планировщиком потоков до ее завершения. В любом многопоточном языке программирования существуют свои способы обеспечения атомарности. В VB .NET проще всего воспользоваться командой SyncLock, при вызове которой передается объектная переменная. Внесите в процедуру ChangeTemperature из предыдущего примера небольшие изменения, и программа заработает нормально:
Private Sub ChangeTemperature() SyncLock (mHouse)
Try
If mHouse.HouseTemp < mHouse.MAXJTEMP -5 Then
Thread.Sleep(200)
mHouse.HouseTemp += 5
Console.WriteLine("Am in " & Me.mName & _
".Current temperature is " & mHouse.HouseTemp)
Elself
mHouse.HouseTemp < mHouse. MAX_TEMP Then
Thread.Sleep(200) mHouse.HouseTemp += 1
Console.WriteLine("Am in " & Me.mName &_ ".Current temperature is " & mHouse.HomeTemp) Else
Console.WriteLineC'Am in " & Me.mName & _ ".Current temperature is " & mHouse.HouseTemp)
' Ничего не делать, температура нормальная
End If Catch tie As ThreadlnterruptedException
' Пассивное ожидание было прервано Catch e As Exception
' Другие исключения
End Try
End SyncLock
End Sub
Код блока SyncLock выполняется атомарно. Доступ к нему со стороны всех остальных потоков будет закрыт, пока первый поток не снимет блокировку командой End SyncLock. Если поток в синхронизируемом блоке переходит в состояние пассивного ожидания, блокировка сохраняется вплоть до прерывания или возобновления работы потока.
Правильное использование команды SyncLock обеспечивает потоковую безопасность вашей программы. К сожалению, злоупотребление SyncLock отрицательно сказывается на быстродействии. Синхронизация кода в многопоточной программе уменьшает скорость ее работы в несколько раз. Синхронизируйте лишь самый необходимый код и снимайте блокировку как можно скорее.
Базовые классы коллекций небезопасны в многопоточных приложениях, но в .NET Framework входят поточно-безопасные версии большинства классов коллекций. В этих классах код потенциально опасных методов заключается в блоки SyncLock. Поточно-безопасные версии классов коллекций следует использовать в многопоточных программах везде, где возникает угроза целостности данных.
Остается упомянуть о том, что при помощи команды SyncLock легко реализуются условные переменные. Для этого потребуется лишь синхронизировать запись в общее логическое свойство, доступное для чтения и записи, как это сделано в следующем фрагменте:
Public Class ConditionVariable
Private Shared locker As Object= New Object()
Private Shared mOK As Boolean Shared
Property TheConditionVariable()As Boolean
Get
Return mOK
End Get
Set(ByVal Value As Boolean) SyncLock (locker)
mOK= Value
End SyncLock
End Set
End Property
End Class