2. Потік
У кожному процесі Windows міститься початковий
"потік", який функціонує в якості вхідної точки для
програми.
Потік - це шлях виконання всередині процесу.
Перший потік, створений точкою входу процесу,
називається главньш потоком.
У будь-якій виконуваній програмі .NET, точка входу
позначається за допомогою методу Main(). При
виклику цього методу головний потік створюється
автоматично.
Andrey Gladky
KspDevelop@gmail.com 2
3. Потік
В рамках платформи .NET не існує прямого
відповідношення "один до одного" між доменами
додатків (AppDomain) і потоками. Фактично певний
домен програми може мати кілька потоків, що
виконуються в кожен конкретний момент часу.
Більш того, конкретний потік не прив'язаний до одного
домену додатка протягом свого часу життя. Потоки
можуть перетинати границі доменів додатків, коли це
вважатимуть за доцільне планувальник потоків
Windows і CLR-середовище .NET.
Andrey Gladky
KspDevelop@gmail.com 3
4. Потік
Незважаючи на те що активні потоки можуть
переміщатися між границями доменів додатків, кожен
потік в будь-який конкретний момент часу може
виконуватися тільки всередині одного домена додатку.
Щоб програмно отримати доступ до домену додатка, в
якому розміщений поточний потік, використувується
статичний метод Thread.GetDomain():
static void ExtractAppDomainHostingThread ()
{
// Отримати домен програми, що розміщує поточний потік.
AppDomain ad = Thread.GetDomain();
}
Andrey Gladky
KspDevelop@gmail.com 4
5. Потік
Один з багатьох болючих аспектів многопоточного
програмування пов'язаний з обмеженим контролем над
використанням потоків операційною системою або
CLR-середовищем.
Наприклад, написавши блок коду, який створює новий
потік виконання, не можна гарантувати, що цей потік
запуститься негайно. Замість цього такий код лише
просить операційну систему запустити потік, як тільки
це буде можливо (зазвичай, коли планувальник потоків
добереться до нього).
Andrey Gladky
KspDevelop@gmail.com 5
6. Потік
Більш того, враховуючи, що потоки можуть
переміщатися між границями додатків і контекстів, коли
це потрібно CLR, ви повинні уявляти, які аспекти
програми є змінюванні в потоках (наприклад, піддаються
багатопотокового доступу), а які операції - атомарними
(змінюванні в потоках операції небезпечні ).
Якщо вихідний потік ще не повністю завершив свою
операцію, другий вхідний потік може побачити об'єкт в
частково зміненому стані. У цей момент другий потік,
по суті, читає фіктивні дані, що безумовно може
призвести до дуже дивних помилок, знайти і налагодити
які навіть ще важче.
Andrey Gladky
KspDevelop@gmail.com 6
7. Багатопоточні додатки
Хоча платформа .NET не може повністю приховати
складності, пов'язані з побудовою надійних
багатопоточних додатків, цей процес все ж значно
спрощений.
Використовуючи типи, визначені всередині простору
імен System.Threading, бібліотеку Task Parallel Library
(TPL) в .NET 4.0 і вище, а також ключові слова asinc і
await мови C# в .NET 4.5, можна працювати з безліччю
потоків, прикладаючи мінімальні зусилля.
Andrey Gladky
KspDevelop@gmail.com 7
8. namespase System.Threading
Andrey Gladky
KspDevelop@gmail.com 8
Тип Призначення
Interlocked Цей тип надає атомарні операції для змінних, розділених між
декількома потоками
Monitor Цей тип забезпечує синхронізацію потокових об'єктів,
використовуючи блокування і очікування/сигнали. Ключове
слово lock мови C # застосовує "за лаштунками" об'єкт
Monitor
Mutex Цей примітив синхронізації може використовуватися для
синхронізації між границями доменів додатків
Parameterized
ThreadStart
Цей делегат дозволяє потоку викликати методи, які
приймають довільну кількість аргументів
Semaphore Цей тип дозволяє обмежити кількість потоків, які можуть
мати доступ до ресурсу або до певного типу ресурсів
одночасно
Thread Цей тип являє потік, що виконується в CLR-середовищі.
Використовуючи етоттіп, можна порождатьдополнітельние
потоки в вихідному домені додатку
ThreadPool Цей тип дозволяє взаємодіяти з підтримуваним CLR пулом
потоків всередині заданого процесу
9. namespase System.Threading
Andrey Gladky
KspDevelop@gmail.com 9
Тип Призначення
ThreadPriority Це перерахування представляє рівень пріоритету потоку
(Highest, Normal і т.д.)
ThreadStart Цей делегат дозволяє вказати метод для виклику в заданому
потоці. На відміну отделегата ParametrizedThreadStart, цільові
методи ThreadStart завжди повинні мати один і той же
прототип
ThreadState Це перерахування задає допустимі стану потоку (Running,
Aborted і т.д.)
Timer Цей тип надає механізм виконання методу через зазначені
інтервали часу
TimerCallback Цей тип делегата використовується в поєднанні з типами
Timer
10. Статичні члени класу Thread
Andrey Gladky
KspDevelop@gmail.com 10
Тип Призначення
CurrentContext Ця властивість, призначена тільки для читання, повертає
контекст, в якому в даний момент виконується потік
CurrentThread Це властивість, призначена тільки для читання, повертає
посилання на поточний виконуванийя потік
GetDomain()
GetDomainID()
Цей метод повертає посилання на поточний домен додатка
або ідентифікатор домену, в якому виконується поточний
потік
Sleep() Цей метод призупиняє поточний потік на зазначений час
11. Члени рівня екземпляра класу Thread
Andrey Gladky
KspDevelop@gmail.com 11
Тип Призначення
IsAlive Повертає буливське значення, яке вказує на те, чи
запущений потік (і поки ще не перерваний і не скасований)
IsBackground Отримує або встановлює значення, яке вказує, чи є даний
потік фоновим
Name Дозволяє встановити дружнє текстове ім'я потоку
Priority Отримує або встановлює пріоритет потоку, який може
приймати значення з перерахування ThreadPriority
ThreadState Отримує стан даного потоку, яке може приймати значення з
перерахування ThreadState
12. Члени рівня екземпляра класу Thread
Andrey Gladky
KspDevelop@gmail.com 12
Тип Призначення
Abort() Вказує CLR-середовищу на необхідність припинення
потоку, як тільки це буде можливо
Interrupt() Перериває (призупиняє) поточний потік на відповідний
період очікування
Join() Блокує викликаючий потік до тих пір, поки вказаний потік
(той, на якому викликаний метод Join ()) не завершиться
Resume() Відновлює раніше призупинений потік
Start() Вказує CLR-середовищу на необхідність запуску потоку, як
тільки це буде можливо
Suspend() Призупиняє потік. Якщо потік вже припинений, виклик
Suspend () не дає ніякого ефекту
14. Ручне створення вторинних потоків
Кроки для ручного створення потоку:
1. Створіть метод, який буде служити точкою входу для нового
потоку.
2. Створіть новий екземпляр делегата ParametrizedThreadStart (або
ThreadStart), передавши конструктору адресу методу, який був
визначений на кроці 1.
3. Створіть об'єкт Thread, передавши конструктору як аргумент
делегат ParametrizedThreadStart/ThreadStart.
4. Встановіть початкові характеристики потоку (ім'я, пріоритет і
т.д.).
5. Викличте метод Thread.Start(). Це призведе до запуску CLR-
середовищем потоку для методу, на який посилається делегат,
створений на кроці 2, при першій же можливості.
Andrey Gladky
KspDevelop@gmail.com 14
16. Роль делегатів .NET
Делегат .NET - це по суті безпечний щодо типів, об'єктно-
орієнтований покажчик на функцію.
З типу делегата, .NET компілятор C# побудує запечатаний клас,
наслідуваного від System.MulticastDelegate.
Ці базові класи надають кожному делегату можливість підтримувати
список адресів методів, які можуть бути викликані в більш пізній
час.
public sealed class BinaryOperation : System.MulticastDelegate
{
public BinaryOperation(object target, uint functionAddress);
public int Invoke(int x, int у) ;
public IAsyncResult BeginInvoke(intx, int y, AsyncCallback cb,
object state);
public int EndInvoke(IAsyncResult result);
}
Andrey Gladky
KspDevelop@gmail.com 16
17. Асинхронна природа делегатів
Коли компілятор C# обробляє ключове слово delegate,
він динамічно генерує клас, який визначає два методи з
іменами BeginInvoke() і EndInvoke().
public sealed class BinaryOperation : System.MulticastDelegate
{
// Використовується для асинхронного виклику методу
public IAsyncResult BeginInvoke(int x, int у, AsyncCallback cb,
object state);
// Використовується для отримання значення, що повертається
// викликаним методом.
public int EndInvoke(IAsyncResult result);
}
Andrey Gladky
KspDevelop@gmail.com 17
18. Інтерфейс IAsyncResult
Метод BeginInvoke() завжди повертає об'єкт, який реалізує
інтерфейс IAsyncResult, в той час як EndInvoke() вимагає
єдиний параметр сумісного з IAsyncResult типу.
Сумісний з IAsyncResult об'єкт, що повертається з
BeginInvoke() - це в основному зв'язуючий механізм, який
дозволяє викликаючому потоку отримати результат виклику
асинхронного методу через EndInvoke() в більш пізній час.
public interface IAsyncResult
{
object AsyncState { get; }
WaitHandle AsyncWaitHandle { get; }
bool CompletedSynchronously { get; }
bool IsCompleted { get; }
}
Метод, який повертає void, можна просто викликати асинхронно і забути.
Andrey Gladky
KspDevelop@gmail.com 18
19. Синхронізація викликаючого потоку
Щоб дозволити викликаючому потоку з'ясовувати, чи
завершив свою роботу асинхронно викликаний метод,
в інтерфейсі IAsyncResult передбачено властивість
IsCompleted. З її допомогою викликаючий потік може
визначати, чи дійсно асинхронний виклик був
завершений, перш ніж звертатися до EndInvoke().
Якщо метод ще не завершився, властивість
IsCompleted повертає false,
Andrey Gladky
KspDevelop@gmail.com 19
20. Роль делегата AsyncCallback
Замість опитування делегата з метою визначення, чи
завершився асинхронно викликаний метод, було б
більш ефективно змусити вторинний потік
інформувати викликаючий потік про завершення
виконання завдання.
Щоб включити таку поведінку, знадобиться передати
методу BeginInvoke() екземпляр делегата
System.AsyncCallback.
Коли передаєтся об'ект AsyncCallback, делегат буде
автоматично викликати вказаний метод по завершенні
асинхронного виклику.
Метод зворотного виклику буде викликаний у вторинному потоці, а не в
первинному.
Andrey Gladky
KspDevelop@gmail.com 20
21. Передача і отримання спеціальних даних стану
Фінальним аспектом асинхронних делегатів, який
повинен бути врахований, є останній аргумент методу
BeginInvoke(). Цей параметр дозволяє передавати
додаткову інформацію про стан методу зворотного
виклику з первинного потоку.
Для отримання цих даних в контексті методу
зворотного виклику використовується властивість
AsyncState вхідного параметра IAsyncResult.
Andrey Gladky
KspDevelop@gmail.com 21