Решение проблемы: синхронизация
В предыдущей программе возникает ситуация, когда результат работы программы зависит от порядка выполнения потоков. Чтобы избавиться от нее, необходимо убедиться в том, что команды типа:
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