|
|||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||
|
Спільна
робота з даними у міру їх
створення
У
багатопотокових застосуваннях часто
зустрічається ситуація, коли потоки не
тільки працюють із загальними даними, але
і чекають їх появи (тобто потік
1 повинен створити дані, перш ніж
потік 2 зможе їх використовувати).
Оскільки дані є загальними,
доступ до них необхідно
синхронізувати. Також необхідно
передбачити засоби для
сповіщення чекаючих потоків про
появу готових даних. Подібна ситуація
зазвичай називається проблемою «постачальник/споживач».
Потік намагається
звернутися до даних, яких
ще немає, тому він повинен передати
управління іншому потоку, що
створює потрібні дані. Проблема
вирішується кодом наступного вигляду:
Зв'язки «постачальник/споживач» зустрічаються
дуже часто, тому в бібліотеках
класів багатопотокового
програмування для
таких ситуацій створюються спеціальні
примітиви. У .NET ці примітиви
називаються Wait і Pulse-pulseal 1 і
є частиною класу Monitor. Малюнок
10.8 пояснює ситуацію, яку
ми збираємося запрограмувати.
У програмі організовуються
три черги потоків: черга
очікування, черга блокування
і черга виконання. Планувальник
потоків не виділяє процесорний
час потокам, що
знаходяться в черзі очікування.
Щоб потоку виділявся час, він
повинен переміститися в чергу
виконання. В результаті робота
додатку організовується набагато
ефективніше, ніж при звичайному опиті
умовної змінної. На псевдокоді
ідіома споживача даних
формулюється так: ' Вхід в синхронізований блок наступного вигляду While
немає даних Перейти в чергу очікування Loop Якщо дані є, обробити їх. Покинути
синхронізований блок Відразу ж після виконання команди
Wait потік припиняється, блокування
знімається, і
потік переходить в чергу очікування.
При знятті блокування потік,
що знаходиться в черзі
виконання, дістає можливість
працювати. З часом один
або декілька заблокованих потоків
створять дані, необхідні
для роботи потоку, що
знаходиться в черзі очікування.
Оскільки перевірка даних
здійснюється в циклі, перехід до
використання даних (після циклу)
відбувається лише за наявності даних,
готових до обробки. На псевдокоді
ідіома постачальника даних виглядає
так: ' Вхід в синхронізований блок вигляду While
дані НЕ потрібні Перейти в чергу очікування Else
Провести дані Після появи готових даних викликати Pulse-pulseall. щоб
перемістити один або декілька
потоків з черги блокування в
чергу виконання. Покинути
синхронізований блок (і
повернутися в чергу виконання) Припустимо,
наша програма моделює сім'ю з
одним батьком, який
заробляє гроші, і дитиною,
яка ці гроші витрачає. Коли
гроші кінчаються, дитині
доводиться чекати приходу нової
суми. Програмна реалізація цієї
моделі виглядає так: 1 Option
Strict On 2 Imports System.Threading 3 Module
Modulel 4 Sub Main() 5 Dim thefamily As
New Family() 6 thefamily.StartltsLife() 7 End
Sub 8 End
fjodule 9 10
Public Class Family 11
Private mmoney As Integer 12
Private mweek As Integer = 1 13 Public Sub
Startltslife() 14 Dim athreadstart
As New Threadstaruaddressof Me.Produce) 15 Dim bthreadstart
As New Threadstaruaddressof Me.Consume) 16 Dim athread As
New Thread(athreadstart) 17 Dim bthread As
New Thread(bthreadstart) 18
athread.Name =
"Produce" 19 athread.Start() 20
bthread.Name =
"Consume" 21 bthread. Start() 22 End
Sub 23 Public Property
Theweek() As Integer 24
Get 25
Return mweek 26 End
Get 27 Set(Byval Value
As Integer) 28 mweek
- Value 29 End
Set 30 End
Property 31 Public Property
Ourmoney() As Integer 32
Get 33
Return mmoney 34 End
Get 35 Set(Byval Value
As Integer) 36
mmoney =Value 37 End
Set 38 End
Property 39 Public Sub
Produce() 40 Thread.Sleep(500) 41
Do 42 Monitor.Enter(Me) 43 Do While Me.OurMoney
> 0 44 Monitor.Wait(Me) 45
Loop 46 Me.OurMoney =1000 47 Monitor.PulseAll(Me) 48 Monitor.Exit(Me) 49
Loop 50 End
Sub 51 Public Sub
Consume() 52 Msgbox("Am
in consume thread") 53
Do 54 Monitor.Enter(Me) 55 Do While Me.OurMoney
= 0 56 Monitor.Wait(Me) 57
Loop 58 Console.WriteLine("Dear parent I just spent all your " & _ money in week "
& Theweek) 59 Theweek += 1 60 If Theweek = 21 *52
Then System.Environment.Exit(0) 61 Me.OurMoney =0 62 Monitor.PulseAll(Me) 63 Monitor.Exit(Me) 64
Loop 65 End
Sub 66 End Class
Метод Startltslife
(рядки 13-22) здійснює підготовку до запуску
потоків Produce і Consume. Найголовніше відбувається
в потоках Produce (рядки 39-50) і Consume
(рядки 51-65). Процедура Sub Produce перевіряє
наявність грошей, і якщо гроші
є, переходить в чергу
очікування. Інакше батько генерує
гроші (рядок 46) і оповіщає об'єкти
в черзі очікування про
зміну ситуації. Врахуйте, що виклик Pulse-pulse
All набуває чинності лише при знятті
блокування командою Monitor.Exit. І
навпаки, процедура Sub Consume перевіряє
наявність грошей, і якщо грошей немає —
оповіщає про це чекаючого батька.
Рядок 60 просто завершує програму
після 21 умовного року; виклик
System. Environment.Exit(0) є .NET-аналогом
команди End (команда End теж
підтримується, але на відміну від System.
Environment. Exit вона не дозволяє повернути
код завершення операційній
системі).
|
|
|||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||