|
|||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||
|
Багатопоточність
в графічних програмах
Наше обговорення багатопоточності в додатках з графічним інтерфейсом почнеться з прикладу, що пояснює, для чого потрібна багатопоточність в графічних застосуваннях. Створіть форму з двома кнопками Start (btnstart) і Cancel (btncancel), як показано на мал. 10.9. При натисненні кнопки Start створюється клас, який містить випадковий рядок з 10 мільйонів символів і метод для підрахунку входжень букви «Е» в цьому довгому рядку. Звернете увагу на застосування класу Stringbuilder, що підвищує ефективність створення довгих рядків. Крок 1 Потік 1 помічає, що даних для нього немає. Він викликає Wait, знімає блокування і переходить в чергу очікування
Крок 2 При знятті блокування потік 2 або потік 3 виходить з черги блокування і входить в синхронізований блок, встановлюючи блокування ШАГЗ Допустимо, потік
3 входить в синхронізований блок,
створює дані і викликає Pulse-pulse All. Відразу ж після його виходу з блоку і зняття блокування потік 1 переміщається в чергу виконання. Якщо потік 3 викликає Pluse, в чергу виконання переходить тільки один потік, при виклику Pluse All в чергу виконання переходять всі потоки.
Мал. 10.8. Проблема
«постачальник/споживач»
Мал. 10.9.
Багатопоточність в простому застосуванні з графічним інтерфейсом Imports System.Text Public
Class Randomcharacters Private m_data As Stringbuilder Private m_countdone As Boolean Private mjength, m_count As Integer Public Sub New(Byval n As Integer) m_length
= n -1 m_data = New Stringbuilder(m_length) Makestring() End
Sub Private Sub Makestring() Dim i As Integer Dim myrnd As New Random() For i =
0 To m_length ' Згенерувати випадкове число від 65 90 ' перетворити його в прописну букву ' і приєднати до об'єкту Stringbuilder m_data.Append(Chr(myrnd.Next(65.90))) Next End Sub Public Sub
Startcount() Getees() End
Sub Private Sub Getees() Dim i As Integer For i =
0 To m_length If
m_data.Chars(i) =
Cchar("E") Then m_count += 1 End If
Next m_countdone = True End
Sub Public Readonly Property Getcount()
As Integer Get If Not (m_countdone)
Then Throw New Exception("Count
not yet done") Else Return m_count End
If End Get
End Property Public Readonly Property Isdone()As
Boolean Get Return m_countdone End Get End Property End
Class З двома кнопками
на формі зв'язується вельми
простий код. У процедурі btn-start_click створюється
екземпляр приведеного вище
класу Randomcharacters, що
інкапсулює рядок з 10
мільйонами символів: Private Sub
btnstart_click(Byval sender As System.Object. Byval e As System.EventArgs)
Handles btnstart.Click Dim RC As New
Randomcharacters(10000000) RC.StartCount() Msgbox("The number of es is " & RC.GetCount) End
Sub Кнопка Cancel
виводить вікно повідомлення: Private Sub btncancel_click(Byval sender As System.Object._ Byval e As System.EventArgs)Handles
btncancel.Click Msgbox("Count Interrupted!") End Sub При запуску
програми і натисненні кнопки Start
з'ясовується, що кнопка Cancel не
реагує на дії користувача,
оскільки безперервний цикл не
дозволяє кнопці обробити
отриману подію. У сучасних
програмах подібне неприпустимо! Можливі два
рішення. Перший варіант, добре
знайомий по попередніх версіях VB,
обходиться без багатопоточності: у
цикл включається виклик Doevents. У .NET ця
команда виглядає так: Application.DoEvents() У нашому
прикладі це визначено небажано
— кому захочеться уповільнювати
програму десятьма мільйонами викликів
Doevents! Якщо замість цього виділити цикл
в окремий потік,
операційна система перемикатиметься між
потоками і кнопка Cancel збереже
працездатність. Реалізація з
окремим потоком приведена нижче.
Щоб наочно показати, що кнопка
Cancel працює, при її натисненні ми
просто завершуємо програму.
|
|
|||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||