модель потоків

Додаток Windows Presentation Foundation (WPF) створено, щоб допомогти розробникам уникнути труднощів при розробці потоків. В результаті, більшості розробників WPF не потрібно писати інтерфейс, який використовує більше одного потоку. Оскільки багатопотокові програми є складними і важко налагоджувати, їх слід уникати, якщо існують однопоточні рішення.

Незалежно від якості архітектури, ніяка платформа UI не зможе запропонувати однопоточні рішення для кожного типу завдань. Додаток WPF досить близько наблизився до вирішення цієї проблеми, але як і раніше є ситуації, в яких кілька потоків покращують швидкодія user interface (UI) або продуктивність програми. Після обговорення деяких основних матеріалів в даному документі розглядаються подібні ситуації і в завершенні обговорюються деякі більш докладні відомості.

У цьому розділі містяться такі підрозділи.

Розробка додатків WPF, як правило, починається з двох потоків: одного для обробки візуалізації, і іншого управління UI. Потік візуалізації ефективно виконується непомітно для користувача в фоновому режимі, тоді як потік UI отримує вхідні дані, обробляє події, виводить зображення на екран і виконує код програми. Більшість додатків використовує один потік UI, хоча в деяких випадках краще використовувати кілька. Пізніше це буде розглянуто на прикладі.

Потік UI ставить в чергу робочі елементи всередині об'єкта, званого Dispatcher. Об'єкт Dispatcher вибирає робочі елементи на основі пріоритетів і виконує кожен з них до завершення. Кожен потік UI повинен мати, по крайней мере, один об'єкт Dispatcher. і кожен об'єкт Dispatcher може виконувати робочі елементи тільки в одному потоці.

Умовою для побудови швидко реагують, зрозумілі користувачеві додатків є максимальне підвищення продуктивності Dispatcher. збереження робочі елементи невеликими. При такому методі елементи ніколи не застарівають в черзі Dispatcher в очікуванні обробки. Будь-яка затримка між вхідними даними і відповідями може розчаровувати користувача.

Як в такому випадку додатки WPF повинні обробляти великі операції? Що якщо код включає великі обчислення або потрібна запит до бази даних на віддаленому сервері? Зазвичай великі операції обробляються в окремому потоці, залишаючи потік UI вільним для обслуговування елементів в черзі Dispatcher. Після завершення великої операції, вона може передати результат назад в потік UI для відображення.

Історично склалося так, що операційна система Windows дозволяє отримувати доступ до елементів UI тільки створив їх потоку. Це означає, що фоновий потік, який відповідає за певну тривалу завдання, не може оновити текстове поле при своєму завершенні. Windows створює це обмеження для забезпечення цілісності компонентів UI. Список може виглядати дивно, якщо його вміст оновлюється фоновим потоком в процесі відображення.

WPF підтримує вбудований механізм взаємного виключення, який здійснює цю координацію. Більшість класів в додатку WPF є похідними від класу DispatcherObject. При створенні DispatcherObject зберігає посилання на об'єкт Dispatcher. пов'язаний з поточним виконуваних потоком. По суті, DispatcherObject зв'язується з потоком, який його створив. Під час виконання програми DispatcherObject може викликати свій відкритий метод VerifyAccess. VerifyAccess перевіряє об'єкт Dispatcher. пов'язаний з поточним потоком, і порівнює його з посиланням на Dispatcher. яка зберігається під час створення. Якщо вони не збігаються, метод VerifyAccess викликає виняток. VerifyAccess призначений для виклику на початку кожного методу, що належить об'єкту DispatcherObject.

Якщо тільки один потік може змінити UI, як фонові потоки взаємодіють з користувачем? Фоновий потік може попросити потік UI виконати операцію від його імені. Він робить це шляхом реєстрації робочого елемента в об'єкті Dispatcher потоку UI. Клас Dispatcher надає два методи для реєстрації робочих елементів: Invoke і BeginInvoke. Обидва методи призначають делегат для виконання. Метод Invoke є синхронним викликом - він не повертає значення до тих пір, поки потік UI закінчить виконання делегата. Метод BeginInvoke є асинхронним і негайно повертає значення.

Об'єкт Dispatcher впорядковує елементи в своїй черзі за пріоритетом. Існують десять рівнів, які можуть бути вказані при додаванні елемента в чергу Dispatcher. Ці пріоритети зберігаються в перерахуванні DispatcherPriority. Докладні відомості про рівні DispatcherPriority можна знайти в Windows SDK документації.

Приклад однопотокового додатки з тривалим виконанням обчислень

Більшість graphical user interfaces (GUIs) витрачають велику частину свого часу, простоюючи в очікуванні подій, які створюються у відповідь на дії користувачів. При уважному програмуванні цей час простою можна використовувати конструктивно, чи не знижуючи швидкість відгуку UI. Передача потокового модель WPF не дозволяє введення переривати операцію, яка відбувається в потоці UI. Це означає, що періодично потрібно повертатися до об'єкта Dispatcher. щоб обробити відкладені події введення, перш ніж вони стануть застарілими.

Розгляньте наступний приклад:

модель потоків

Це просте додаток шукає прості числа, починаючи від трьох і далі. При натисканні користувачем кнопки Start починається пошук. Коли програма знаходить просте число, вона оновлює користувальницький інтерфейс. У будь-якій точці користувач може зупинити пошук.

При всій простоті операції пошук простих чисел може відбуватися нескінченно, що представляє деякі труднощі. Якби обробка всіх операцій пошуку виконувалася всередині обробника подій натискання кнопки, потік UI ніколи б не отримав можливість для обробки інших подій. UI не міг би відповісти на вхідні дані або обробити повідомлення. Він би ніколи не оновив відображення і не відповів би на натискання кнопки.

Можна провести пошук простого числа в окремому потоці, але тоді довелося б мати справу з проблемами синхронізації. За допомогою однопотокові підходу можна безпосередньо оновити підпис, в якій перераховано найбільше просте число.

Якщо розбити задачу обчислення на керовані фрагменти, можна періодично повертатися до об'єкта Dispatcher і подій обробки. Можна дати додатком WPF можливість оновлювати і обробляти введення.

Кращим способом розбиття часу обробки між обчисленням і обробкою події є управління обчисленням з об'єкта Dispatcher. За допомогою методу BeginInvoke можна запланувати перевірку простого числа в тій же черзі, з якої приходять події UI. У наведеному прикладі запланована перевірка тільки одного простого числа в кожен момент часу. Після завершення перевірки простого числа негайно планується наступна перевірка. Ця перевірка виконується тільки після обробки очікують подій UI.

модель потоків

За допомогою цього механізму додаток Microsoft Word виконує перевірку орфографії. Перевірка орфографії виконується в фоновому режимі, використовуючи час простою потоку UI. Давайте подивимося на код.

У наступному прикладі показаний код XAML, який створює інтерфейс користувача.

Цей метод перевіряє, чи є таке непарне число простим. Якщо воно просте, метод безпосередньо оновлює bigPrime TextBlock. щоб відобразити його результат пошуку. Ми можемо зробити так тому, що обчислення відбувається в тому ж потоці, який був використаний для створення компонента. Якщо використовувати окремий потік для обчислень, довелося б застосувати більш складний механізм синхронізації і виконувати оновлення в потоці UI. Ця ситуація буде продемонстрована далі.

Повний вихідний код прикладу див. У розділі Single-Threaded Application with Long-Running Calculation Sample ( "Приклад однопотокового додатки з тривалим виконанням обчислень").

Обробка блокує операції з фоновим потоком

Обробка блокування операцій в графічному додатку може виявитися важким завданням. Ми не будемо викликати методи блокування з обробників подій, так як додаток буде зупинено. Можна використовувати окремий потік для обробки цих операцій, але потім потрібно буде синхронізуватися з потоком UI, оскільки не можна безпосередньо змінити GUI з робочого потоку. Вставку делегатів в об'єкт Dispatcher потоку UI можна здійснити за допомогою методу Invoke або BeginInvoke. В результаті, ці делегати будуть виконані з дозволом на зміну елементів UI.

У цьому прикладі ми імітуємо виклик віддаленої процедури, який отримує прогноз погоди. Ми використовуємо окремий робочий потік для виконання цього виклику і плануємо метод оновлення в об'єкті Dispatcher потоку UI при завершенні.

модель потоків

Схожі статті