|
|||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||
|
Головна
небезпека (загальні дані)
До
справжнього моменту
розглядався єдиний безпечний
випадок використання потоків —
наші потоки не змінювали загальних даних.
Якщо вирішити зміну загальних
даних, потенційні помилки
починають плодитися в
геометричній прогресії і позбавити від
них програму стає
набагато важчим. З іншого боку,
якщо заборонити модифікацію загальних
даних різними потоками,
багатопотокове програмування .NET практично
не відрізнятиметься від обмежених
можливостей Vb6.
Вашій увазі пропонується
невелика програма, яка
демонструє виникаючі проблеми, не заглиблюючись
в зайві подробиці. У
цій програмі моделюється будинок, в
кожній кімнаті якого
встановлений термостат. Якщо температура на 5
і більше градусів за Фаренгейтом
(близько 2,77 градусів за
Цельсієм) менше покладеною, ми
наказуємо системі опалювання підвищити температуру
на 5 градусів; інакше
температура підвищується тільки на 1
градус. Якщо поточна температура
більше або рівна заданою,
зміна не проводиться.
Регулювання температури в кожній
кімнаті здійснюється окремим
потоком із затримкою
200-мілісекунди. Основна робота
виконується наступним фрагментом: If mhouse.HouseTemp
< mhouse.MAX_TEMP = 5 Then Try Thread.Sleep(200) Catch
tie As Threadlnterruptedexception ' Пасивне очікування було перерване Catch e
As Exception ' Інші
виключення End Try mhouse.HouseTemp +- 5
' І так далі Нижче приведений
повний початковий текст програми.
Результат показаний на мал. 10.6:
температура в будинку досягла 105
градусів за Фаренгейтом (40,5 градуса
за Цельсієм)! 1 Option
Strict On 2 Imports System.Threading 3 Module
Modulel 4 Sub Main() 5 Dim myhouse As New
House(l0) 6 Console. Readline() 7 End
Sub 8 End
Module 9 Public
Class House 10
Public Const Max_temp As Integer = 75 11
Private mcurtemp As Integer = 55 12 Private mrooms()
As Room 13 Public Sub New(Byval
numofrooms As Integer) 14 Redim mrooms(numofrooms
= 1) 15 Dim i
As Integer 16 Dim athreadstart
As Threading.ThreadStart 17 Dim
athread As Thread 18 For i
= 0 To numofrooms -1 19
Try 20 mrooms(i)=
NewRoom(Me, mcurtemp,cstr(i)&"throom") 21 athreadstart - New Threadstart(Addressof _ mrooms(i).CheckTempInRoom) 22 athread =New
Thread(athreadstart) 23 athread.Start() 24 Catch
E As Exception 25 Console.WriteLine(E.StackTrace) 26 End
Try 27
Next 28 End
Sub 29 Public Property
Housetemp()As Integer 30 . Get 31
Return mcurtemp 32 End
Get 33 Set(Byval Value
As Integer) 34
mcurtemp = Value 35 End Set 36 End
Property 37 End
Class 38
Public Class Room 39
Private mcurtemp As Integer 40
Private mname As String 41
Private mhouse As House 42 Public Sub New(Byval thehouse As House Byval temp As
Integer, Byval roomname As String) 43
mhouse = thehouse 44
mcurtemp = temp 45 mname
= roomname 46 End
Sub 47 Public Sub
Checktempinroom() 48 Changetemperature() 49 End
Sub 50 Private Sub
Changetemperature() 51
Try 52 If mhouse.HouseTemp
< mhouse.MAX_TEMP - 5 Then 53 Thread.Sleep(200) 54 mhouse.HouseTemp
+- 5 55 Console.WriteLine("Am
in " & Me.mName & _ 56 ".Current
temperature is "&mhouse.HouseTemp) 57 . Elself mhouse.HouseTemp
< mhouse.MAX_TEMP Then 58 Thread.Sleep(200) 59 mhouse.HouseTemp
+= 1 60 Console.WriteLine("Am
in " & Me.mName & _ 61 ".Current
temperature is " & mhouse.HouseTemp) 62
Else 63 Console.WriteLine("Am
in " & Me.mName & _ 64 ".Current
temperature is " & mhouse.HouseTemp) 65 '
Нічого не робити, температура нормальна 66 End
If 67 Catch
tae As Threadlnterruptedexception 68 '
Пасивне очікування було перерване 69 Catch
e As Exception 70 '
Інші виключення 71 End
Try 72 End
Sub 73 End Class
Мал. 10.6. Проблеми
багатопоточності У процедурі Sub
Main (рядки 4-7) створюється «будинок» з
десятьма «кімнатами». Клас House
встановлює максимальну
температуру 75 градусів за
Фаренгейтом (близько 24 градусів за
Цельсієм). У рядках 13-28 визначається
досить складний конструктор будинку.
Ключовими для розуміння програми
є рядки 18-27. Рядок 20
створює черговий об'єкт кімнати,
при цьому конструктору передається
посилання на об'єкт будинку, щоб об'єкт
кімнати при необхідності міг до
нього звернутися. Рядки 21-23
запускають десять потоків для
регулювання температури в кожній
кімнаті. Клас Room визначається в
рядках 38-73. Посилання на об'єкт House зберігається
в змінній mhouse в конструкторі
класу Room (рядок 43). Код перевірки і
регулювання температури (рядки 50-66)
виглядає просто і природно, але
як ви незабаром переконаєтеся, це
враження брехливе! Звернете
увагу на те, що цей код
поміщений в блок Try-catch, оскільки в
програмі використовується метод Sleep. Навряд чи хто-небудь
погодиться жити при температурі в
105 градусів за Фаренгейтом (40,5 24
градусів за Цельсієм). Що ж
відбулося? Проблема пов'язана з
наступним рядком: If mhouse.HouseTemp
< mhouse.MAX_TEMP - 5 Then А відбувається
наступне: спочатку температуру
перевіряє потік 1. Він бачить, що
температура дуже низка, і
піднімає її на 5 градусів. На
жаль, перед підвищенням
температури потік 1 уривається і
управління передається потік 2.
Потік 2 перевіряє ту ж саму
змінну, яка ще не була
змінена потоком 1. Таким чином,
потік 2 теж готується підняти
температуру на 5 градусів, але
зробити цього не встигає і теж
переходить в стан очікування.
Процес продовжується до тих пір,
поки потік 1 не активізується і не
перейде до наступної команди —
підвищення температури на 5 градусів.
Підвищення повторюється при
активізації всіх 10 потоків, і
мешканцям будинку доведеться погано.
|
|
|||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||