SlideShare une entreprise Scribd logo
1  sur  99
Крос-платформне програмування
Лекція 13
Процеси та нитки.
Багатопоточність у .NET Framework
20 травня, 2015
Примітка: частину слайдів лекції підготовлено за матеріа-
лами курсу prof. J.Kubiatowicz (CS162 ©UC Berkeley)
http://inst.eecs.berkeley.edu/~cs162/fa10/
Визначення
• Однозадачність: одна задача/процес виконується в один і
той же час
– MS/DOS, ранні Macintosh, ОС з пакетним виконанням (batch
processing)
– Проста в реалізації
– Має сенс для персональних комп’ютерів?
• Багатозадачність: більш ніж одна задача/процес в один і
той самий час
– Multics, UNIX/Linux, OS/2, Windows NT/2000/XP,
Mac OS
• Багатоядерність ⇒ Багатозадачність, правда?
Основні проблеми багатозадачності
• Конкуруючий доступ до ресурсів:
– апаратних: один CPU, одна ОП, один пристрій вводу/виводу
(миша, клавіатура, HDD)
– багатозадачного API: з точки зору програміста його
програма має ексклюзивний доступ до усіх ресурсів
• Всю активність координує ОС
– Багато користувачів, ввід/вивід тощо. Як не заплутатись?
• Ідея: Застосувати абстракцію «віртуальної машини»
– Замість реальних пристроїв використовувати “віртуальні”
» Існує багато різних носіїв даних з різними апаратними
інтерфейсами, ідеальний інтерфейс – файлова система
– Уперше реалізовано Дейкстрою у системі «THE System»
Як створити ілюзію одночасного виконання?
CPU3CPU2CPU1
Загальна пам’ять
• Припустимо є 1 процесор. Як створити ілюзію, що їх багато?
– Розділити в часі!
• Кожний “віртуальний процесор” має стан (або контекст):
– Лічильник інструкцій (PC), Вказівник на стек (SP)
– Регістри (Цілі, з плаваючою комою та інше)
• Як переключатися з одного процесора на інший?
– Зберегти PC, SP, та регістри в поточному блоці стану
– Завантажити PC, SP, та регістри з нового блоку
• Що змушує нитку змінити контекст?
– Добровільний виклик yield(); переривання: таймер,
ввід/вивід та інше.
CPU1 CPU2 CPU3 CPU1 CPU2
Час
Властивості такої простої техніки багатозадачності
• Усі віртуальні CPU розділяють усі ресурси
– Ввід/вивід – той же
– Пам’ять – теж
• Наслідки розділення ресурсів:
– Кожна нитка має доступ до пам’яті будь-якої іншої
(добре для обміну, але нитки не захищені)
– Нитки можуть розділяти навіть код
(добре для обміну, погано для захисту)
– Чи можуть нитки зруйнувати внутрішні функції ОС?
• Така (незахищена) модель застосовується в:
– Вбудованих системах (PocketPC)
– Windows 3.1/Machintosh (switch тільки через yield)
– Windows 95—ME? (switch через yield та таймер)
Як захистити нитки одну від одної?
• Три важливі речі:
1. Захист пам'яті
» Нитка не має доступу до усієї пам'яті
1. Захист пристроїв вводу/виводу
» Нитка не має доступу до усіх пристроїв
1. Захист процесора:
Примусове переключення контексту
» Застосувати таймер
» Треба не дати можливості програмам
користувача відключати таймер
CPU MMU
Віртуальні
адреси
Фізичні
адреси
Трансляція адрес
• Адресний простір
– Множина доступних адрес
– Кожна програма (програма) і ядро ОС можуть
потенційно бути в різних адресних просторах.
• Ідея:
– Перетворення “віртуальних адрес” (які отримав
процесор) в фізичні адреси (в ОП)
– Зазвичай перетворення відбувається в обладнанні,
Memory Management Unit (MMU)
Віртуальний адресний простір
Прог. 1
Віртуальний
адресний
простір 1
Прог. 2
Віртуальний
адресний
простір 2
Код
Дані
Купа
Стек
Код
Дані
Купа
Стек
Дані 2
Стек 1
Купа 1
Купа і
стеки ОС
Код 1
Стек 2
Дані 1
Купа 2
Код 2
Код ОС
Дані ОСТаблиця перетворень 1 Таблиця перетворень 2
Фізичний адресний простір
Адреснийпростір
Адресний простір програми
• Адресний простір ⇒ множина
доступних адрес + стани,
пов'язані з ними:
– Для 32-бітного CPU 232
= 4 млрд.
адрес
• Що відбувається під час читання
чи запису адрес?
– Можливо, нічого
– Можливо – звичайна пам'ять
– Можливо, запис ігнорується
– Можливо, виконується ввід/вивід
» (ввід/вивід через пам'ять)
– Можливо, виникає виключення
(fault)
Як забезпечується захист між процесами
• Ідея: Зачинити процесі по своїх
“палатах” та вилучити “гострі”
предмети
– Не всі інструкції можна виконувати
– Чи редагувати системні таблиці
– Обмежити доступ до пам’яті: не
можна впливати на “чужі” процеси
» Побічний ефект: Зменшення
ефективності доступу до I/O,
яке виконується через ОП
– Ще?
• Які виникають проблеми?
– Я переключати CPU між процесами?
» Ніби хворі і лікарі в психічній лікарні – це одна і та
сама людина.
– Як програмам взаємодіяти?
Два режими виконання – Kernel+User
• Обладнання дає можливість мінімум двох режимів
виконання:
– Ядра (або “Kernel”, “supervisor” чи “protected”)
– Користувача (“User”)
• В режимі користувача доступні не всі інструкції:
– Напримлад: неможна редакувати таблиці розподілу
пам’яті
» Якщо буде спроба ⇒ Виняткова ситуація (segfault?)
• Як перейти з режиму користувача в режим ядра?
– Системні виклики, переривання, виняткові ситуації
Традиційні процеси в UNIX
• Процес: Абстракція операційної системи,
необхідна для виконання однієї програми
– Часто називають “важкими процесами”
– Формально: один потік команд у своєму
адресному просторі
• Складається з двох частин:
– Послідовний потік команд
» Код виконується як єдиний, послідовний потік
команд
» Включно зі станами регістрів процесора
– Захищені ресурси:
» Стани пам'яті (вміст адресного простору)
» Стани вводу/виводу (напр. дескриптори файлів)
• В традиційному UNIX немає багатопоточності в
межах процесу - він складається тільки з однієї
нитки
Process
Control
Block
Як розділити процесор між процесами?
• Поточний стан процесу зберігається в
process control block (PCB):
– Це – знімок стану процесора та захищеного
середовища
– Кожного моменту активний тільки один PCB
• Надавати процесорний час різним процесам
(Планування):
– Тільки один процес «виконується» в один
момент часу
– Надавати більше часу більш важливим
процесам
• Надавати частину ресурсів різним процесам
(Захист):
– Контрольований доступ до інших ресурсів
– Механізми (наприклад):
» Трансляція адрес: Кожному процесу – свій
адресний простір
» Подвійний режим виконання в процесорі
(Kernel/Use). Доступ до вводу/виводу через
системні виклики
Переключення CPU від процесу до процесу
• Також відоме як «переключення контексту» (“context switch”)
• Код, виконуваний в ядрі – накладні витрати на переключення
– Накладні витрати встановлюють мінімальний практичний час
між переключеннями
– Менше накладних витрат з використанням SMT/hyperthreading,
але… конкуренція за процесорний час
Діаграма станів процесу
• Протягом виконання процес міняє стани
– new: Процес тільки-но створено
– ready: Процес очікує виконання
– running: Виконується в даний момент
– waiting: Очікує на зовнішню подію
– terminated: Процес завершив виконання
Планування виконання процесів
• Протягом зміни стану, PCB переходять з черги у чергу
– Прийняття рішення щодо черговості - планування
– Можливі різні алгоритми
Що необхідно для створення процесу?
• Побудувати новий PCB
– Недорого
• Налаштувати таблицю віртуальних адрес
– Дорожче
• Копіювати дані з батьківського процесу? (Unix fork() )
– Семантика fork() в UNIX потребує копіювання даних
батьківського процесу в дочірній процес + стани
вводу/виводу
– Спочатку – дуже дорого
– Набагато дешевше при «копіюванні під час запису»
• Копіювати стани вводу/виводу (дескриптори файлів
тощо)
– Середня вартість
Сучасні “полегшені” процеси або нитки
• Нитка: послідовний потік команд всередині процесу
(інколи називають “Lightweight process” або LWP)
– Процес, як і раніше, у своєму адресному просторі
– Немає захисту між нитками
• Багатонитковість: одна програма (процес), що
складається з кількох різних ниток
– Інколи називають багатозадачністю
• Навіщо розмежовувати поняття процесу та нитки?
– Вважайте «нитка» - частина процесу
(багатопоточність)
– Але без свого адресного простору (захисту)
– Важкий процес ≡ Процес з однією ниткою
Одно- та багатониткові процеси
• Процес – абстракція операційної системи, необхідна
для виконання однієї багатониткової програми
• Нитка – незалежний потік команд у (віртуальному)
процесорі
Стани нитки
• Стан, загальний для всіх ниток у процесі/адресному
просторі
– Вміст віртуальної пам'яті (глобальні змінні, купа)
– Стан вводу/виводу (файлової системи, мережних
з'єднань, інше)
• Стан, особистий для кожної нитки
– Зберігається в TCB ≡ Thread Control Block
– Регістри CPU (включно з лічильником команд, PC)
• Стек виконання
– Параметри, локальні змінні
– PC повернення зберігаються у стеку протягом виклику
одних процедур з інших
Приклад стеку викликів
• Стек містить тимчасові результати
• Дозволяє робити рекурсивні
виклики
• Є критичним для сучасних мов
програмування
A(int tmp) {
if (tmp<2)
B();
printf(tmp);
}
B() {
C();
}
C() {
A(2);
}
A(1);
A: ret=C+1
tmp=2Stack
Pointer
Stack Growth
A: ret=exit
tmp=1
B: ret=A+2
C: ret=B+1
Приклад однониткової програми
• Уявіть наступну програму на Сі:
main() {
ComputePI(“pi.txt”);
PrintSchedule(“clist.text”);
}
• Яка поведінка очікується?
– Програма не надрукує розклад
– Чому? ComputePI ніколи не завершиться
Використання ниток
• Версія цієї програми з використанням ниток:
main() {
CreateThread(ComputePI(“pi.txt”));
CreateThread(PrintSchedule(“clist.text”));
}
• Що зробить “CreateThread”?
– Створить незалежний потік, що виконує вказану процедуру
• Яка поведінка тут?
– Тепер ми хоч побачимо список занять
– Це має виглядати так, ніби у нас 2 процесори
CPU1 CPU2 CPU1 CPU2
Time
CPU1 CPU2
Як виглядає пам'ять, якщо є дві нитки
• Якщо ми зупинимо програму та подивимось на
неї у дебагері, ми побачимо
– Два набори регістрів CPU
– Два стеки
• Питання:
– Як розмістити стеки один
відносно одного?
– Який встановити максимальний
розмір стеку?
– Що буде, якщо нитки це порушать?
– Як спіймати такі порушення?
Код
Глоб. дані
Купа
Стек 1
Стек 2
Адреснийпростір
Цикл життя нитки (чи процесу)
• Протягом виконання нитка змінює стани:
– new: Нитку тільки-но створено
– ready: Нитка очікує на виконання
– running: Нитка в процесі виконання на CPU
– waiting: Нитка очікує на деяку подію
– terminated: Нитка закінчила виконання, але не стек та
TCB ще не звільнено
• В ОС нитки представлені за допомогою TCB
– TCB організовано в черги, в залежності від станів
Черги готових ниток та різноманітні черги пристроїв
вводу/виводу
• Нитка не виконується ⇒ TCB в одній із черг
– Окрема черга для кожного пристрою/сигналу/події
– У кожної черги свої правила планування
Other
State
TCB9
Next
Registers
Other
State
TCB6
Next
Registers
Other
State
TCB16
Next
Registers
Other
State
TCB8
Next
Registers
Other
State
TCB2
Next
Registers
Other
State
TCB3
Next
Registers
Head
Tail
Head
Tail
Head
Tail
Head
Tail
Head
Tail
Ready
Queue
Tape
Unit 0
Disk
Unit 0
Disk
Unit 2
Ether
Netwk 0
Цикл диспетчеризації
• Концептуально, цикл диспетчеризації в ОС
виглядає так:
Loop {
RunThread();
ChooseNextThread();
SaveStateOfCPU(curTCB);
LoadStateOfCPU(newTCB);
}
• Це нескінченний цикл
– Можна говорити, що це головне, що робить
ОС
• Чи необхідно хоч коли-небудь вийти з циклу???
– Коли?
Виконати нитку
Розглянемо першу частину: RunThread()
• Як виконати нитку?
– Завантажити її стан (регістри, PC, SP) в процесор
– Завантажити середовище (віртуальний адресний
простір, і т.п.)
– Перейти до PC
• Як диспетчеру назад отримати контроль?
– Внутрішні події: нитка передає контроль добровільно
– Зовнішні події: нитка примусово переключається
Внутрішні події
• Блокування при вводі/виводі
– Під час виконання вводу/виводу нитка автоматично
віддає процесор
• Очікування на сигнал від іншої нитки
– Нитка просить очікування та віддає процесор
• Нитка викликає yield()
– Нитка віддає процесор добровільно
computePI() {
while(TRUE) {
ComputeNextDigit();
yield();
}
}
Зовнішні події
• Що відбудеться, якщо нитка ніколи не буде
використовувати ввід/вивід, ніколи не буде очікувати та
віддавати контроль?
– Можна використовувати програму ComputePI, щоб
захопити всі ресурси та не віддавати процесор?
» Що, якщо вона навіть нічого не пише в консоль?
– У диспетчера має бути спосіб відновити контроль!
• Відповідь: Використання зовнішніх подій
– Переривання: обладнання посилає сигнал
процесору, перериваючи поточну програму та
переходячи до режиму ядра
– Таймер: як будильник, який виконується кожні
кілька мілісекунд
• Якщо ми зробимо так, що зовнішні події виникають
досить часто, ми забезпечимо, щоб диспетчер працював
інколи, коли це необхідно
Зберігання/Виклик стану («Переключення контексту»)
Switch(tCur,tNew) {
/* Зберегти стару нитку */
TCB[tCur].regs.r7 = CPU.r7;
…
TCB[tCur].regs.r0 = CPU.r0;
TCB[tCur].regs.sp = CPU.sp;
TCB[tCur].regs.retpc = CPU.retpc;/*адреса
повернення*/
/* Завантажити та виконати нову нитку */
CPU.r7 = TCB[tNew].regs.r7;
…
CPU.r0 = TCB[tNew].regs.r0;
CPU.sp = TCB[tNew].regs.sp;
CPU.retpc = TCB[tNew].regs.retpc;
return; /* Return to CPU.retpc */
}
Вибір наступної нитки
• Як диспетчер вирішує, що запустити далі?
– Якщо немає готових ниток – кружляє в циклі
диспетчера
» Альтернатива – створити нитку “idle” (бездіяльність)
» Може переключити процесор в режим низького
енергоспоживання
– Якщо всього одна нитка - просто
– Більше однієї нитки – використовувати планувальні
правила
• Можливі правила:
– LIFO (last in/first out, останнім увійшов, першим
вийшов):
– Обирати випадково
– FIFO (first in/first out, першим увійшов, першим
вийшов):
» Це «чесно»
– Черга з пріоритетами:
» Сортувати чергу готовності за пріоритетом (прописаним у
TCB)
Нитки режиму ядра проти ниток режиму
користувача
• Поки ми говорили про нитки в режимі ядра
– Це ті нитки, що підтримують ядро ОС
– Кожна нитка може виконуватись чи очікувати незалежно
– Один процес може містити кілька ниток, що очікують на
різне
• Недолік ниток рівня ядра: дорого
– Для планування необхідно переключатися у режим ядра
• Існує полегшене рішення: Нитки рівня користувача
– Користувацька програма надає планувальник та пакет
роботи з нитками
– Можна мати кілька ниток користувача на 1 нитку ядра
– Нитки користувача можуть керуватись непримусово одна
відносно іншої (переключення тільки по yield())
– Дешево
• Недоліки:
– Коли одна нитка ядра блокується на вводі/виводі,
блокуються всі нитки рівня користувача
– Ядро не враховує множину ниток рівня користувача під час
планування
Теоретичні моделі
1 до 1
Багато до 1 Багато до багатьох
Основні простори імен FCL
System
System.Data System.Xml
System.Web
Globalization
Diagnostics
Configuration
Collections
Resources
Reflection
Net
IO
Threading
Text
ServiceProcess
Security
Design
ADO
SQLTypes
SQL
XPath
XSLT
Runtime
InteropServices
Remoting
Serialization
Serialization
Configuration SessionState
Caching Security
Services
Description
Discovery
Protocols
UI
HtmlControls
WebControls
System.Drawing
Imaging
Drawing2D
Text
Printing
System.Windows.Forms
Design ComponentModel
Багатопоточність
• Потік (threads) – незалежний шлях виконання,
здатний виконуватися одночасно з іншими потоками
• Складається з:
– Ядро потоку (thread kernel) – структура даних, набір
властивостей якої описує потік. Містить контекст
потоку (700-1240 байт)
– Блок оточення потоку ТЕВ (Thread Environment Block) –
місце у пам'яті, виділене і ініціалізоване в режимі
користувача, містить ланцюжок обробки виключень (4-
8 Кбайт)
– Стек режиму користувача (user-mode stack) – застосо-
вується для переданих у методи локальних змінних і
аргументів, містить адресу, що показує, звідки почне
виконання потік після того, як поточний метод поверне
управління (1 Мбайт)
– Стек режиму ядра (kernel-mode stack) – використо-
вується, коли код застосування передає аргументи на
функцію ОС, що знаходиться в режимі ядра (12-24
Кбайт)
– Повідомлення про створення і завершення потоків
Багатопоточність у програмах
Переваги багатопоточності
• прискорити відгук користувацького інтерфейсу
програми на дії користувача, шляхом
розміщення довготривалих задач в окремі
потоки
• повного використання потужностей
багатоядерних машин та гіперпотокових
одноядерних процесорів
• ізоляції одного коду від іншого
Клас System.Threading
• System.Threading – типи для створення багатопо-
точних застосувань
– System.Diagnostics - типи для роботи з процесами
Клас Опис
Interlocked Надає атомарні операції для змінних, що
загальнодоступні кільком потокам
Monitor Надає механізм для синхронізації доступу до об'єктів
Mutex Примітив синхронізації, який також може
використовуватися в міжпроцесорній синхронізації
Semaphore Обмежує кількість потоків, які можуть одночасно
отримувати доступ до ресурсу або пулу ресурсів
Thread Створює і контролює потік, задає пріоритет і
повертає статус
ThreadPool Надає пул потоків, який може використовуватися
для створення робочих елементів, обробки
асинхронного вводу/виводу, очікування від імені
інших потоків і обробки таймерівTimer Надає механізм для виконання методу в задані
інтервали часу
Члени класу System.Threading.Thread
• Властивості
– Name – задає зрозуміле рядкове ім’я потоку
– IsBackground – вказує, чи є потік фоновим
• Об'єкт Thread реалізує усі стандартні операції
над нитками
Метод Опис
Abort Виклик даного методу завершує потік
Interrupt Перериває роботу потоку, що перебуває у стані
WaitSleepJoin
Join Блокує викликаючий потік до завершення потоку
Resume Відновлює призупинену роботу потоку
Sleep Статичний метод. Блокує поточний потік на задану
кількість мілісекунд
Start Дозволяє планувати виконання потоку (запускати
потік)
Suspend Припиняє роботу потоку; якщо робота потоку вже
припинена, не має ефекту
Члени класу System.Threading.Thread (2)
Делегат Опис
ParameterizedThreadStart Представляє метод, що виконується в
об'єкті Thread
ThreadStart Представляє метод, що виконується в
об'єкті Thread
Перерахування Опис
ThreadPriority Задає пріоритет виконання потоку Thread
{ Highest, AboveNormal, Normal, BelowNormal,
Lowest }
ThreadState Визначає набір всіх можливих станів виконання
для потоків Thread
Приклад: створення потоку на основі
делегата ThreadStart
Приклад: створення потоку на основі
делегата ParameterizedThreadStart
Активні та фонові потоки
• Активні потоки або потоки переднього плану
(foreground threads) виконуються завжди до
свого завершення, а якщо основний потік
завершив свою роботу, тоді програма очікує
завершення усіх вторинних пріоритетних потоків
• Фонові потоки (background threads) або потоки-
демони автоматично зупиняються та
знищуються після завершення усіх активних
потоків
• Активні та фонові потоки не є синонімами
первинних та вторинних (робочих) потоків
– Первинний потік – перший потік, створений точкою
входу процеса
– Робочий потік – додатковий вторинний потік,
породжений первинним потоком (напр., функцією
Thread.Start())
Приклад: активні та фонові потоки
Література
• Рихтер Дж. CLR via C#. Програм-
мирование на платформе Microsoft
.NET Framework 4.0 на языке C#. 3-е
изд. – СПб.: Питер, 2012. – 928 с.
• Троелсен Э. Язык программирования
C# 5.0 и платформа .NET 4.5. 6-е
изд., - М.: Издательский дом
“Вильямс”, 2013. - 1312 с.
• Корисні ресурси
– http://msdn.microsoft.com/ru-ru/library/
Крос-платформне програмування
Лекція 14
Багатопоточність у .NET Framework.
Загальні властивості паралелізму
27 травня, 2015
Примітка: частину слайдів лекції підготовлено за матеріа-
лами курсу prof. J.Kubiatowicz (CS162 ©UC Berkeley)
http://inst.eecs.berkeley.edu/~cs162/fa10/
Thread pool
• Пул потоків - набір доступних застосуванням
потоків
– Черга задач на виконання
– N ниток у замороженому стані
– Щойно з’являється задача, вона призначається першій
вільній нитці
– Після завершення задачі нитка не знищується, а
заморожується та помічається як вільна
Thread pool (2)
• Типи потоків у пулі
– Робочі потоки (worker thread) – використовуються,
коли застосування вимагає від пулу виконання
асинхронної обчислювальної операції
– Потоки введення-виведення (I/O thread) – служать
для повідомлення коду про завершення асинхронної
операції введення-виведення
– state - параметр (дані стану)
– саllback – метод, який буде викликаний потоком з
пулу та повинен відповідати делегату
System.Threading.WaitCallback
– ThreadPool.SetMaxThreads() – загальна кількість
ниток
– ThreadPool.GetAvailableThreads() – кількість
вільних ниток
Приклад: використання пулу потоків
Приклад: використання пулу потоків (2)
Переваги та обмеження пулу потоків
• Переваги
– Пул потоків управляє потоками ефективно,
зменшуючи кількість потоків, що створюються,
запускаються та зупиняються
– Використовуючи пул потоків, можна зосередитися на
вирішенні завдання, а не на інфраструктурі потоків
застосування
• Обмеження
– Усі потоки у пулі є фоновими
– Не можна змінювати пріоритет або ім'я потоку, що
знаходиться у пулі
– Потоки у пулі підходять для виконання тільки
коротких завдань
Thread vs ThreadPool
• Клас Thread
– Дороге створення та
видалення ниток
– Повний контроль над
об’єктом (пріорітети,
заморозка/пробудження,
Thread.Abort())
• Клас ThreadPool
– Дешеве створення
ниток
– Вбудована черга задач
– Відсутність контролю
над ниткою
Класи Timer платформи .NET Framework
• У .NET Framework доступно кілька класів Timer, які
можуть застосовуватися для забезпечення виклику
методів по закінченню певного проміжку часу
Простір імен Опис
System.Threading У його конструкторі класу Timer можна
передавати делегат, що викликається через
вказаний інтервал часу
System.Timers Клас Timer є компонентом, тому що
успадкований від базового класу Component.
Використовує клас Timer
System.Threading.Timer, але замість делегатів
надає механізм на основі подійSystem.Windows.Forms Елементи керування WindowsForms
прив'язуються до потоку, що їх створив.
Зворотній виклик в цей потік дозволяє
здійснювати клас Timer з простору імен
System.Windows.FormsSystem.Web.UI Клас Timer є розширенням Ajax та
призначений для використання з веб-
сторінками
System.Windows.Threading Клас DispatcherTimer застосовується в WPF-
застосуваннях та виконується в рамках потоку
для користувача інтерфейсу
Використання таймера
Один з перевантажених конструкторів таймера:
Приклад виклику таймеру для виводу поточного часу:
Приклад: використання таймера (2)
• Метод Change()
служить для зміни
проміжку після
створення об’єкту
Timer
Багатопроцесорність vs Багатозадачності
• Порівняння визначень:
– Багатопроцесорність ≡ Багато CPU
– Багатозадачність ≡ Багато задач чи процесів
– Багатонитковість ≡ Багато ниток на 1 процес
• Що означає, що дві нитки виконуються “одночасно”?
– Планувальник в ОС може обрати будь-який механізм
виконання та переключення: FIFO, Random, …
– Диспетчер може вирішувати, виконувати нитку до
завершення чи розбити її на великі чи малі частини
A B C
BA ACB C BБагатозадачність
A
B
C
Багатопроцесорність
Приклад: спільно використовувані ресурси
Очікуваний результат:
Отриманий результат:
Коректність систем з нитками
• Диспетчер може розмістити у часі нитки як завгодно, аби
програма працювала правильно
– Чи можна протестувати програму на коректність?
– Як дізнатись, що програма працює?
• Незалежні нитки:
– Нитки не мають нічого спільного
– Детермінованість ⇒ Вхідні дані визначають результат
– Відтворюваність ⇒ Можна повторити стартові умови,
ввід/вивід
– Порядок виконання ниток не має значення (якщо працює
switch())
• Взаємодія ниток:
– Нитки мають спільні дані
– Недетермінованість
– Невідтворюваність
• Недетермінованість та невідтворюваність означає, що помилки
виникають не завжди та випадково
– Інколи називають “Heisenbugs” (хайзенбагз)
Навіщо ниткам взаємодіяти
• Люди взаємодіють; комп'ютери нам допомагають, тому вони
мають взаємодіяти
– За аналогією, неповторність та недетермінізм роботи людей –
відома проблема «точних планів»
• Перевага 1: Загальні ресурси
– Один комп'ютер, багато користувачів
– Один рахунок, багато банкоматів (ATM)
» Що, якби ATM-и обновлялись тільки уночі?
– Вбудовані системи (керування роботом: координувати руку та
долоню)
• Перевага 2: Прискорення
– Сумістити ввід/вивід та обчислення
» Багато файлових систем зчитують дані на перед
– Багатопроцесорність – розбити програму на паралельні
частини
• Перевага 3: Модульність
– Важливіше, ніж здається на перший погляд
– Розділити велику програму на маленькі частини
» Під час компіляції в UNIX gcc викл. «cpp | cc1 | cc2 | as | ld»
» Гнучкість під час розширення системи
Проблема знаходиться на низькому рівні
• Коли нитки працюють з незалежними даними,
порядок виконання не має значення
Thread A Thread B
x = 1; y = 2;
• Але коли (спочатку y = 12)
Thread A Thread B
x = 1; y = 2;
x = y+1; y = y*2;
– Які можливі значення x?
• Або, які можливі значення x з наступного прикладу?
Thread A Thread B
x = 1; x = 2;
– X може бути 1 або 2 (не детерміновано!)
– Може бути навіть 3, якщо робота з пам'яттю побітова
» Нитка A пише 0001, B пише 0010.
» Порядок запису ABABABBA дає 3!
• Багатониткові програми мають працювати коректно при за будь-якої
послідовності виконання ниток
– Нитки що взаємодіють створюють недетермінованість і
неповторюваність
– Важко відлагоджувати, якщо погано спроектовано!
• Приклад: Therac-25
– Інструмент радіаційної терапії
» Програмний контроль
прискорювача
» Програмний контроль
дозування
– Помилка в програмі призвела
до смерті кількох пациентів
» Рейсові умови виникали
через погане проектування
» “Було визначено, що швидкість вводу даних під час редагування була
ключовим фактором виникнення помилкової ситуації: передозування
виникало, якщо дані вводилися надто швидко.”
» http://dl.dropbox.com/u/14787239/lec/add/TheracNew.pdf
Умови коректності
Запуск першого космічного шатла
• Запуск першого шатлу було перервано за 20 хвилин перед
запланованим стартом
• У шатлі було 5 комп'ютерів:
– 4 виконували “Primary Avionics
Software System” (PASS)
» Асинхронна в реальному часі
» Керування усіма системами
» Результати синхронізувались і порівнювались кожні 3 - 4 мс
– П'ятий комп'ютер був “Backup Flight System” (BFS)
» синхронізується з PASS за необхідністю
» запрограмований іншою командою розробників
• Запуск припинено, тому що BFS і PASS не співпали
– Існував 1/67 шанс, що PASS вийде з синхронізації
– Помилка в ініціюючому коді PASS
» Запит на відкладену ініціалізацію був покладений в чергу
таймера
» В результаті черга таймера не була пустою в очікуваний час
– Помилка не була знайдена при ретельному моделюванні
– http://dl.dropbox.com/u/14787239/lec/add/garman_bug_81.pdf
PASS
BFS
Синхронізація
• Синхронізація між потоками одного процесу
– оператор lock
– клас Monitor
– клас Interlocked
• Синхронізація між потоками різних процесів
– клас Mutex
– клас Semaphore
– клас Event
– клас ReaderWriterLockSlim
Оператор lock
Синхронізуючий об’єкт або
маркер обов’язково повинен
мати тип-посилання
Блок синхронізації
• Не дозволяє жодному потоку увійти у блок синхро-
нізації, коли в ньому знаходиться інший потік
– При спробі входу іншого потоку в заблокований код буде
потрібно дочекатися зняття блокування об'єкта
– Маркер використовується для визначення області блокування
(рекомендується уникати блокування типу public або
екземплярів, якими код не керує)
Приклад: використання оператору lock
Клас Monitor
Запис
lock (obj)
{
A++;
}
перетворюється компілятором у такий код:
Monitor.Enter(obj);
try
{
A++;
}
finally
{
Monitor.Exit(obj);
}
синхронізований регіон для obj
синхронізований регіон для obj
блокування об’єкта obj ;
позначає початок критичної секції
звільнення об’єкта obj
Методи класу Monitor
• Методи Wait(), Pulse() та PulseAll() можуть
викликатися тільки з заблокованого фрагмента блоку
• Можливі форми методу Wait()
Метод Опис
Wait() Звільняє блокування об’єкта для того, щоб дозволити
іншим потокам здійснити блокування та отримати
доступ до об’єкта. Повертає булеве значення, яке
вказує на успіх очікування чи завершення його по
закінченню таймаутаPulse() Посилає сигнал потоку, що стоїть першим у черзі
очікування. Очікуючий потік переміщується в чергу
готових та може отримати блокування для об’єкта
PulseAll() Посилається сигнал всім потокам, і вони
переміщуються в чергу готових
TryEnter() Робить спробу отримати доступ до об’єкта протягом
часу, вказаного у вхідних параметрах. Якщо доступ
отримано, то метод повертає true, якщо ні – false
Прикад: використання методів класу Monitor
• У порівнянні з попе-
редніми прикладами
час виконання цієї
програми буде на
декілька порядків
більшим
Приклад: використання методів класу Monitor (2)
Клас Interlocked
Метод Опис
int newV Interlocked.Increment(ref intV); Збільшує значення змінної на одиницю
та повертає ще й на додаток нове
значення
int newV Interlocked.Decrement(ref intV); Зменшує значення змінної на одиницю
double newA = Interlocked.Exchange(ref doubA, 90.5); Присвоює змінній вказане значення та
повертає початкове значення
Interlocked.CompareExchange(ref i, 99, 83); Якщо значення i рівне 83, тоді замінити
його на 99
• Надає атомарні операції
для змінних, загально-
доступних декільком
потокам
• Є набагато більш швидким
підходом у порівнянні з
іншими підходами щодо
забезпечення синхронізації
На минулій лекції:
багатопоточність у програмах
Паралельна система
• Паралельна програма невід’ємна від паралельної
архітектури
• Паралельна система – це
паралельна програма + паралельна архітектура
– Час виконання T залежить від
» Кількості процесорів (N)
» Розміру задачі (n)
– Головна умова коректності
» Результат має бути детермінованим
» Результат не має залежати від кількості процесорів
Паралельна система як чорна скриня
• Розглянемо приклади виконання паралельних
програм
• Час виконання в залежності від кількості
0
200
400
600
800
1000
1200
0 5 10 15 20 25 30 35
Job1
0
200
400
600
800
1000
1200
0 5 10 15 20 25 30 35
Job2
Головні властивості
• Основні показники якості паралельної програми
» T1 – час на одному процесорі, TN – час на N процесорах
• Параметри прискорення і ефективність - суперечать
– Збільшуючи N, збільшуємо прискорення S, але
зменшуємо ефективність
– Ідеальна ефективність, E=1, досягається при N=1
• Властивість
– Ідеальне прискорення S=N
» Іноді буває надлінійне прискорення (S>N)
(в цьому курсі не розглядаємо)
Прискорення: Ефективність:
N
N
T
T
S 1
=
N
N
N
NT
T
N
S
E 1
==
Повертаючись до початкового прикладу
Прискорення Ефективність
• Висновки:
– Загально погана якість паралельних програм
– Друга програма трохи краща
0
1
2
3
4
5
6
7
8
9
10
0 5 10 15 20 25 30 35
Job1
Job2
0
0,2
0,4
0,6
0,8
1
1,2
0 5 10 15 20 25 30 35
Job1
Job2
Послідовна частина
• Нехай
– η - частина алгоритму, яка може бути розпаралелена
– σ - частина алгоритму, яка не може бути
розпаралелена (послідовна частка)
• Тоді час виконання паралельної програми
• Прискорення буде мати вигляд
1T
N
TN 





+=
η
σ
( )
( ) NNTN
T
T
T
S
N
N
/)1(
1
/)1(
)1(
/ 1
11
σσσσ
σσ
ησ
ησ
−+
=
−+
−+
=
+
+
==
Обмеження паралелізму
• Закон Амдала (1967) «Прискорення обмежене
послідовною частиною»
– Нехай σ – послідовна частка
( ) 26.102095.05.01%5,20 =+=⇒== NSN σ
N
SN
)1(
1
σσ −+
≤
σ
1
lim ≤
∞→
N
N
S
Приклад:
Критика закону Амдала
• Закон Амдала припускає, що послідовна
частина в програмі є постійною
– Але вона часто залежить від розміру задачі!
» Зазвичай, чим більша задача, тим менша частка,
яку не можна розпаралелити
» Наприклад, припустимо, ми моделюємо погодні
умови
• Якщо у нас буде більший паралельний
комп'ютер, ми захочемо збільшити задачу,
щоб отримати точніші результати
• Закон Амдала сильно песимістичний!
– Насправді це уповільнило розвиток
паралельних систем у 1960-70х роках
Масштабована послідовна частина
• Нехай n – розмір задачі. Тоді послідовна та
паралельні частини σs
і η будуть мати вигляд:
σs
(n) + η(n) = 1
• Час виконання послідовної версії програми буде
T(1) = σs
(n)T(N) + η(n)NT(N)
• Тоді прискорення буде мати вигляд
S(N) = σs
(n)+η(n)N = N-σs
(n)(N-1)
• Таке прискорення зветься масштабованим
Якщо n прямує до нескінченності, σs
(n) прямує до 0, а S
прямує до N.
Реабілітація паралелізму
• Закон Густавсона-Барсиса (1988) – «Збільшуючи
розміри задачі, збільшуємо паралельну частку,
досягаємо необхідного
прискорення»:
• де σs -
масштабована
послідовна частка
• Наприклад:
1) При N=10 послідовна частка склала усього 3%
загального часу
2) Для того, щоб досягнути прискорення в 7 разів при
N=8 треба збільшити розмір задачі s так, щоб послідовна
частка дорівнювала:
( ) s
NN NNS σ⋅−−≤ 1
( ) ( ) ( ) ( ) 14.018781 =−−=−−≤ NSN N
s
Nσ
( ) 73.903.011010 =⋅−+=⇒ NS
Порівняння законів Амдала і Густавсона-Барсиса
• Закон Амдала: “Якщо ви вже проїхали 50 км зі
швидкістю 50 км/год, ви не можете проїхати
100 км з середньою швидкістю, більшою, ніж
100 км/год”
• Закон Густавсона-Барсиса: “Якщо можна обирати
відстань, то незалежно від того, скільки часу ми
їхали і з якою швидкістю, ми можемо досягти
будь-якої бажаної середньої швидкості”
0 50 км 100 км
0 50 км ? км
Критика закону Густавсона-Барсиса
• Припускає, що для будь-якої задачі можна знайти
дані великого розміру
– Але для багатьох задач це не так
• Для задач нелінійної складності збільшення розмірів
задачі не дає такого суттєвого збільшення
паралелізму
– Наприклад, для задач складністю O(n3
) збільшення
кількості операцій удвічі збільшує розмір задачі лише
на 26%
• Закон Густавсона-Барсиса сильно оптимістичний!
– І це сприяло розвитку паралельних систем у
1970-80х роках
Реальна картина
• Для будь-якої паралельної задачі з обмінами виникають
комунікаційні витрати
– вони збільшуються разом з N
• Тому існує таке N, на якому досягається оптимальне
прискорення (для фіксованого розміру задачі!)
Графік
прогнозованого та
експериментального
прискорення
Навіщо?
• Розв’язувати задачі швидше
– (це очевидна мета) Ще?
• Розв’язувати великі задачі
– Збільшення деталізації
– Підвищення точності
• Витримати велике навантаження
– Розподілити вхідний потік запитів
– Розподілити місце збереження даних
• Використання віддалених ресурсів
– Використання усіх доступних вільних ресурсів
(ГРІД)
Закон Мура – тренд розвитку IT-індустрії
2X транзисторів на чипі кожні 1.5 років
Закон Мура
Гордон Мур (співзасновник
Intel) спрогнозував в 1965
році, що кількість
транзисторів на чипі буде
подвоюватись кожні 18
місяців.
Мікропроцесори стають
меншими, щільнішими
та потужнішими.
Сприйняття закону Мура у 60-х роках
• У 1960-х роках закон Мура виглядав
безглуздо, тому що прогнозував через сорок
років “маленькі домашні комп’ютери”
Закон Мура
(с) UC Berkeley CS10 Beauty and Joy of Computing
На попередній лекції: багатоядерні процесори
• “БАГАТО-ядерні” процесори
– 64? 128? Важко спрогнозувати границю
• Як їх застосувати?
– Нехай 2 ядра - на відео і аудіо
– Нехай 1 ядро - на текстовий редактор, 1 - на браузер
– 76 – на захист від вірусів???
• Треба застосувати паралелізм на усіх рівнях
• Intel 80-ядерний чип (02.2007)
– 80 простих ядер
– 2 АЛУ / ядро
– Решіткова мережа
– 100 мільйонів транзисторів
– 65nm технології
• Intel Single-Chip Cloud
Computer (04.2010)
– 24 “плитки” по 2 ядра
– 24-клітинна решіткова мережа
– 4 DDR3 контролери пам’яті
– Апаратна підтримка обміну повідомленнями
(с) John Kubiatowicz Adapted from UCB
Що робити з оцим?
Застосування в різних галузях
Застосування: обробка великого потоку звертань
• Веб-сервери та пошукові системи
• Фінансові системи
• Зберігання та обробка даних
великого обсягу
• Cloud
Застосування паралельних систем:
3D-анімація
• 1. Якість та роздільна здатність понад усе
(3D-мультиплікація)
• 2. Візуалізація у реальному часі (30 fps)
(3D-ігри)
Моделювання Анімація Освітлення Візуалізація
web.engr.oregonstate.edu/~mjb/intro2009/
Застосування: моделювання фізичних процесів
• Кінцево-різницеві та
кінцево-елементні
– Погода
– Медицина
– Будівництво
– Сейсміка
– Гідродинаміка
– Аеродинаміка
• Взаємодія часток
– Молекулярна динаміка
– Моделювання всесвіту
Література
• Рихтер Дж. CLR via C#. Програм-
мирование на платформе Microsoft
.NET Framework 4.0 на языке C#. 3-е
изд. – СПб.: Питер, 2012. – 928 с.
• Троелсен Э. Язык программирования
C# 5.0 и платформа .NET 4.5. 6-е
изд., - М.: Издательский дом
“Вильямс”, 2013. - 1312 с.
• Корисні ресурси
– http://msdn.microsoft.com/ru-ru/library/
Дякую за увагу
На апаратному рівні паралелізм усюди!
• Сучасні процесори складаються приблизно з 109
транзисторів
– Очевидно, що вони мають працювати паралельно
– Наскільки програмісту треба це контролювати?
• Як однопроцесорні архітектури виділяють
паралелізм?
– Автоматично: шукають у потоці команд
ILP – Instruction Level Parallelism
• До 2002 року задача архітектури комп'ютера –
приховати паралелізм від програміста,
компілятора, ОС
• Приклади ILP технологій:
– Конвеєрне виконання: одночасне виконання різних частин
інструкції
– Суперскалярна архітектура: виконувати >1 інструкції за
такт
– VLIW: Вказати паралелізм прямо в інструкції
– Векторні операції: вказувати групу незалежних операцій
– Невпорядковане виконання інструкцій (OOO, Out of order
execution)
Засоби розробки для паралельних систем
• OpenMP – для багатоядерних систем
– (та БАГАТОядерних систем)
• MPI – переважно систем з розподіленою пам'яттю
– Але є перспективи використання для будь-яких
систем
– “MPI – assembly language of parallel programming”
• OpenCL/CUDA – для графічних прискорювачів
– Яка з цих двох технологій переможе – зараз не
зрозуміло
– Але є потенційна можливість використання OpenMP
• ГРІД? – розподілені обчислення, поки що не
мають домінуючої технології
Інструменти паралельного програмування
• Мови паралельного програмування
– Складно створити та розвивати
– Потребує перенавчання
» І переписати усі програми
– Приклади: Occam, CHILL
• Розширення послідовних мов
– Середня складність створення
– Вивчати треба тільки особливості мови
– Приклади: Brook, CUDA, OpenCL
• Розширення компіляторів
– Вивчати треба тільки паралельні конструкції
» Треба чітка стандартизація специфікацій
– Приклад: OpenMP
• Бібліотеки та інтерфейси (API)
– Вивчати треба тільки API
– Паралелізм треба описувати у явному вигляді
» Але це складно
– Приклади: PVM(1989), MPI (1994)

Contenu connexe

Similaire à Lec13 14 багатопоточнiсть

Lec15 архiтектура та проектування компонентних систем
Lec15 архiтектура та проектування компонентних системLec15 архiтектура та проектування компонентних систем
Lec15 архiтектура та проектування компонентних систем
cit-cit
 
пк апаратне забезпечення іс 2
пк апаратне забезпечення іс 2пк апаратне забезпечення іс 2
пк апаратне забезпечення іс 2
iDeus3D
 
Сучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютера
Сучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютераСучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютера
Сучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютера
Максим Павленко
 
будова пк
будова пкбудова пк
будова пк
Yura_Shilo
 
будова пк
будова пкбудова пк
будова пк
Yura_Shilo
 

Similaire à Lec13 14 багатопоточнiсть (20)

Computers and Computing Works lecture №5
Computers and Computing Works lecture №5Computers and Computing Works lecture №5
Computers and Computing Works lecture №5
 
Изучение интерфейсов операционных систем с помощью Embedded System
Изучение интерфейсов операционных систем с помощью Embedded SystemИзучение интерфейсов операционных систем с помощью Embedded System
Изучение интерфейсов операционных систем с помощью Embedded System
 
Lec15 архiтектура та проектування компонентних систем
Lec15 архiтектура та проектування компонентних системLec15 архiтектура та проектування компонентних систем
Lec15 архiтектура та проектування компонентних систем
 
OS_Lecture_01_2017.pdf
OS_Lecture_01_2017.pdfOS_Lecture_01_2017.pdf
OS_Lecture_01_2017.pdf
 
пк апаратне забезпечення іс 2
пк апаратне забезпечення іс 2пк апаратне забезпечення іс 2
пк апаратне забезпечення іс 2
 
Сучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютера
Сучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютераСучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютера
Сучасні інформаційні технології. Лекція 2. Архітектура персонального комп’ютера
 
IIHE-Lecture-3_1
IIHE-Lecture-3_1IIHE-Lecture-3_1
IIHE-Lecture-3_1
 
архітектура комп
архітектура компархітектура комп
архітектура комп
 
Будова комп'ютера
Будова комп'ютераБудова комп'ютера
Будова комп'ютера
 
Лекція №5
Лекція №5Лекція №5
Лекція №5
 
Лекція №1
Лекція №1Лекція №1
Лекція №1
 
Лекція №1
Лекція №1Лекція №1
Лекція №1
 
Planyvannja
PlanyvannjaPlanyvannja
Planyvannja
 
Загальні принципи розроблення АРМ оператора на базі SCADA/HMI
Загальні принципи розроблення АРМ оператора на базі SCADA/HMIЗагальні принципи розроблення АРМ оператора на базі SCADA/HMI
Загальні принципи розроблення АРМ оператора на базі SCADA/HMI
 
Операційні системи
Операційні системи Операційні системи
Операційні системи
 
Анімовані компоненти та навігація
Анімовані компоненти та навігаціяАнімовані компоненти та навігація
Анімовані компоненти та навігація
 
урок 5
урок 5урок 5
урок 5
 
будова пк
будова пкбудова пк
будова пк
 
будова пк
будова пкбудова пк
будова пк
 
Golovinskiy hpc day 2011
Golovinskiy hpc day 2011Golovinskiy hpc day 2011
Golovinskiy hpc day 2011
 

Plus de cit-cit (20)

лекція 5
лекція 5лекція 5
лекція 5
 
лаборатор. 10
лаборатор. 10лаборатор. 10
лаборатор. 10
 
лекція 19
лекція 19лекція 19
лекція 19
 
лекція 18
лекція 18лекція 18
лекція 18
 
лекція 17
лекція 17лекція 17
лекція 17
 
лекція 16
лекція 16лекція 16
лекція 16
 
лекція 12
лекція 12лекція 12
лекція 12
 
лекція 11
лекція 11лекція 11
лекція 11
 
лекція 10
лекція 10лекція 10
лекція 10
 
лаборатор. 15
лаборатор. 15лаборатор. 15
лаборатор. 15
 
лаборатор. 14
лаборатор. 14лаборатор. 14
лаборатор. 14
 
лаборатор. 13
лаборатор. 13лаборатор. 13
лаборатор. 13
 
лаборатор. 12
лаборатор. 12лаборатор. 12
лаборатор. 12
 
лаборатор. 11
лаборатор. 11лаборатор. 11
лаборатор. 11
 
лаборатор. 9
лаборатор. 9лаборатор. 9
лаборатор. 9
 
лаборатор. 8
лаборатор. 8лаборатор. 8
лаборатор. 8
 
лаборатор. 7
лаборатор. 7лаборатор. 7
лаборатор. 7
 
лекція 15 (pdf.io)
лекція 15 (pdf.io)лекція 15 (pdf.io)
лекція 15 (pdf.io)
 
лекція 14 (pdf.io)
лекція 14 (pdf.io)лекція 14 (pdf.io)
лекція 14 (pdf.io)
 
лекція 13 (pdf.io)
лекція 13 (pdf.io)лекція 13 (pdf.io)
лекція 13 (pdf.io)
 

Lec13 14 багатопоточнiсть

  • 1. Крос-платформне програмування Лекція 13 Процеси та нитки. Багатопоточність у .NET Framework 20 травня, 2015 Примітка: частину слайдів лекції підготовлено за матеріа- лами курсу prof. J.Kubiatowicz (CS162 ©UC Berkeley) http://inst.eecs.berkeley.edu/~cs162/fa10/
  • 2. Визначення • Однозадачність: одна задача/процес виконується в один і той же час – MS/DOS, ранні Macintosh, ОС з пакетним виконанням (batch processing) – Проста в реалізації – Має сенс для персональних комп’ютерів? • Багатозадачність: більш ніж одна задача/процес в один і той самий час – Multics, UNIX/Linux, OS/2, Windows NT/2000/XP, Mac OS • Багатоядерність ⇒ Багатозадачність, правда?
  • 3. Основні проблеми багатозадачності • Конкуруючий доступ до ресурсів: – апаратних: один CPU, одна ОП, один пристрій вводу/виводу (миша, клавіатура, HDD) – багатозадачного API: з точки зору програміста його програма має ексклюзивний доступ до усіх ресурсів • Всю активність координує ОС – Багато користувачів, ввід/вивід тощо. Як не заплутатись? • Ідея: Застосувати абстракцію «віртуальної машини» – Замість реальних пристроїв використовувати “віртуальні” » Існує багато різних носіїв даних з різними апаратними інтерфейсами, ідеальний інтерфейс – файлова система – Уперше реалізовано Дейкстрою у системі «THE System»
  • 4. Як створити ілюзію одночасного виконання? CPU3CPU2CPU1 Загальна пам’ять • Припустимо є 1 процесор. Як створити ілюзію, що їх багато? – Розділити в часі! • Кожний “віртуальний процесор” має стан (або контекст): – Лічильник інструкцій (PC), Вказівник на стек (SP) – Регістри (Цілі, з плаваючою комою та інше) • Як переключатися з одного процесора на інший? – Зберегти PC, SP, та регістри в поточному блоці стану – Завантажити PC, SP, та регістри з нового блоку • Що змушує нитку змінити контекст? – Добровільний виклик yield(); переривання: таймер, ввід/вивід та інше. CPU1 CPU2 CPU3 CPU1 CPU2 Час
  • 5. Властивості такої простої техніки багатозадачності • Усі віртуальні CPU розділяють усі ресурси – Ввід/вивід – той же – Пам’ять – теж • Наслідки розділення ресурсів: – Кожна нитка має доступ до пам’яті будь-якої іншої (добре для обміну, але нитки не захищені) – Нитки можуть розділяти навіть код (добре для обміну, погано для захисту) – Чи можуть нитки зруйнувати внутрішні функції ОС? • Така (незахищена) модель застосовується в: – Вбудованих системах (PocketPC) – Windows 3.1/Machintosh (switch тільки через yield) – Windows 95—ME? (switch через yield та таймер)
  • 6. Як захистити нитки одну від одної? • Три важливі речі: 1. Захист пам'яті » Нитка не має доступу до усієї пам'яті 1. Захист пристроїв вводу/виводу » Нитка не має доступу до усіх пристроїв 1. Захист процесора: Примусове переключення контексту » Застосувати таймер » Треба не дати можливості програмам користувача відключати таймер
  • 7. CPU MMU Віртуальні адреси Фізичні адреси Трансляція адрес • Адресний простір – Множина доступних адрес – Кожна програма (програма) і ядро ОС можуть потенційно бути в різних адресних просторах. • Ідея: – Перетворення “віртуальних адрес” (які отримав процесор) в фізичні адреси (в ОП) – Зазвичай перетворення відбувається в обладнанні, Memory Management Unit (MMU)
  • 8. Віртуальний адресний простір Прог. 1 Віртуальний адресний простір 1 Прог. 2 Віртуальний адресний простір 2 Код Дані Купа Стек Код Дані Купа Стек Дані 2 Стек 1 Купа 1 Купа і стеки ОС Код 1 Стек 2 Дані 1 Купа 2 Код 2 Код ОС Дані ОСТаблиця перетворень 1 Таблиця перетворень 2 Фізичний адресний простір
  • 9. Адреснийпростір Адресний простір програми • Адресний простір ⇒ множина доступних адрес + стани, пов'язані з ними: – Для 32-бітного CPU 232 = 4 млрд. адрес • Що відбувається під час читання чи запису адрес? – Можливо, нічого – Можливо – звичайна пам'ять – Можливо, запис ігнорується – Можливо, виконується ввід/вивід » (ввід/вивід через пам'ять) – Можливо, виникає виключення (fault)
  • 10. Як забезпечується захист між процесами • Ідея: Зачинити процесі по своїх “палатах” та вилучити “гострі” предмети – Не всі інструкції можна виконувати – Чи редагувати системні таблиці – Обмежити доступ до пам’яті: не можна впливати на “чужі” процеси » Побічний ефект: Зменшення ефективності доступу до I/O, яке виконується через ОП – Ще? • Які виникають проблеми? – Я переключати CPU між процесами? » Ніби хворі і лікарі в психічній лікарні – це одна і та сама людина. – Як програмам взаємодіяти?
  • 11. Два режими виконання – Kernel+User • Обладнання дає можливість мінімум двох режимів виконання: – Ядра (або “Kernel”, “supervisor” чи “protected”) – Користувача (“User”) • В режимі користувача доступні не всі інструкції: – Напримлад: неможна редакувати таблиці розподілу пам’яті » Якщо буде спроба ⇒ Виняткова ситуація (segfault?) • Як перейти з режиму користувача в режим ядра? – Системні виклики, переривання, виняткові ситуації
  • 12. Традиційні процеси в UNIX • Процес: Абстракція операційної системи, необхідна для виконання однієї програми – Часто називають “важкими процесами” – Формально: один потік команд у своєму адресному просторі • Складається з двох частин: – Послідовний потік команд » Код виконується як єдиний, послідовний потік команд » Включно зі станами регістрів процесора – Захищені ресурси: » Стани пам'яті (вміст адресного простору) » Стани вводу/виводу (напр. дескриптори файлів) • В традиційному UNIX немає багатопоточності в межах процесу - він складається тільки з однієї нитки
  • 13. Process Control Block Як розділити процесор між процесами? • Поточний стан процесу зберігається в process control block (PCB): – Це – знімок стану процесора та захищеного середовища – Кожного моменту активний тільки один PCB • Надавати процесорний час різним процесам (Планування): – Тільки один процес «виконується» в один момент часу – Надавати більше часу більш важливим процесам • Надавати частину ресурсів різним процесам (Захист): – Контрольований доступ до інших ресурсів – Механізми (наприклад): » Трансляція адрес: Кожному процесу – свій адресний простір » Подвійний режим виконання в процесорі (Kernel/Use). Доступ до вводу/виводу через системні виклики
  • 14. Переключення CPU від процесу до процесу • Також відоме як «переключення контексту» (“context switch”) • Код, виконуваний в ядрі – накладні витрати на переключення – Накладні витрати встановлюють мінімальний практичний час між переключеннями – Менше накладних витрат з використанням SMT/hyperthreading, але… конкуренція за процесорний час
  • 15. Діаграма станів процесу • Протягом виконання процес міняє стани – new: Процес тільки-но створено – ready: Процес очікує виконання – running: Виконується в даний момент – waiting: Очікує на зовнішню подію – terminated: Процес завершив виконання
  • 16. Планування виконання процесів • Протягом зміни стану, PCB переходять з черги у чергу – Прийняття рішення щодо черговості - планування – Можливі різні алгоритми
  • 17. Що необхідно для створення процесу? • Побудувати новий PCB – Недорого • Налаштувати таблицю віртуальних адрес – Дорожче • Копіювати дані з батьківського процесу? (Unix fork() ) – Семантика fork() в UNIX потребує копіювання даних батьківського процесу в дочірній процес + стани вводу/виводу – Спочатку – дуже дорого – Набагато дешевше при «копіюванні під час запису» • Копіювати стани вводу/виводу (дескриптори файлів тощо) – Середня вартість
  • 18. Сучасні “полегшені” процеси або нитки • Нитка: послідовний потік команд всередині процесу (інколи називають “Lightweight process” або LWP) – Процес, як і раніше, у своєму адресному просторі – Немає захисту між нитками • Багатонитковість: одна програма (процес), що складається з кількох різних ниток – Інколи називають багатозадачністю • Навіщо розмежовувати поняття процесу та нитки? – Вважайте «нитка» - частина процесу (багатопоточність) – Але без свого адресного простору (захисту) – Важкий процес ≡ Процес з однією ниткою
  • 19. Одно- та багатониткові процеси • Процес – абстракція операційної системи, необхідна для виконання однієї багатониткової програми • Нитка – незалежний потік команд у (віртуальному) процесорі
  • 20. Стани нитки • Стан, загальний для всіх ниток у процесі/адресному просторі – Вміст віртуальної пам'яті (глобальні змінні, купа) – Стан вводу/виводу (файлової системи, мережних з'єднань, інше) • Стан, особистий для кожної нитки – Зберігається в TCB ≡ Thread Control Block – Регістри CPU (включно з лічильником команд, PC) • Стек виконання – Параметри, локальні змінні – PC повернення зберігаються у стеку протягом виклику одних процедур з інших
  • 21. Приклад стеку викликів • Стек містить тимчасові результати • Дозволяє робити рекурсивні виклики • Є критичним для сучасних мов програмування A(int tmp) { if (tmp<2) B(); printf(tmp); } B() { C(); } C() { A(2); } A(1); A: ret=C+1 tmp=2Stack Pointer Stack Growth A: ret=exit tmp=1 B: ret=A+2 C: ret=B+1
  • 22. Приклад однониткової програми • Уявіть наступну програму на Сі: main() { ComputePI(“pi.txt”); PrintSchedule(“clist.text”); } • Яка поведінка очікується? – Програма не надрукує розклад – Чому? ComputePI ніколи не завершиться
  • 23. Використання ниток • Версія цієї програми з використанням ниток: main() { CreateThread(ComputePI(“pi.txt”)); CreateThread(PrintSchedule(“clist.text”)); } • Що зробить “CreateThread”? – Створить незалежний потік, що виконує вказану процедуру • Яка поведінка тут? – Тепер ми хоч побачимо список занять – Це має виглядати так, ніби у нас 2 процесори CPU1 CPU2 CPU1 CPU2 Time CPU1 CPU2
  • 24. Як виглядає пам'ять, якщо є дві нитки • Якщо ми зупинимо програму та подивимось на неї у дебагері, ми побачимо – Два набори регістрів CPU – Два стеки • Питання: – Як розмістити стеки один відносно одного? – Який встановити максимальний розмір стеку? – Що буде, якщо нитки це порушать? – Як спіймати такі порушення? Код Глоб. дані Купа Стек 1 Стек 2 Адреснийпростір
  • 25. Цикл життя нитки (чи процесу) • Протягом виконання нитка змінює стани: – new: Нитку тільки-но створено – ready: Нитка очікує на виконання – running: Нитка в процесі виконання на CPU – waiting: Нитка очікує на деяку подію – terminated: Нитка закінчила виконання, але не стек та TCB ще не звільнено • В ОС нитки представлені за допомогою TCB – TCB організовано в черги, в залежності від станів
  • 26. Черги готових ниток та різноманітні черги пристроїв вводу/виводу • Нитка не виконується ⇒ TCB в одній із черг – Окрема черга для кожного пристрою/сигналу/події – У кожної черги свої правила планування Other State TCB9 Next Registers Other State TCB6 Next Registers Other State TCB16 Next Registers Other State TCB8 Next Registers Other State TCB2 Next Registers Other State TCB3 Next Registers Head Tail Head Tail Head Tail Head Tail Head Tail Ready Queue Tape Unit 0 Disk Unit 0 Disk Unit 2 Ether Netwk 0
  • 27. Цикл диспетчеризації • Концептуально, цикл диспетчеризації в ОС виглядає так: Loop { RunThread(); ChooseNextThread(); SaveStateOfCPU(curTCB); LoadStateOfCPU(newTCB); } • Це нескінченний цикл – Можна говорити, що це головне, що робить ОС • Чи необхідно хоч коли-небудь вийти з циклу??? – Коли?
  • 28. Виконати нитку Розглянемо першу частину: RunThread() • Як виконати нитку? – Завантажити її стан (регістри, PC, SP) в процесор – Завантажити середовище (віртуальний адресний простір, і т.п.) – Перейти до PC • Як диспетчеру назад отримати контроль? – Внутрішні події: нитка передає контроль добровільно – Зовнішні події: нитка примусово переключається
  • 29. Внутрішні події • Блокування при вводі/виводі – Під час виконання вводу/виводу нитка автоматично віддає процесор • Очікування на сигнал від іншої нитки – Нитка просить очікування та віддає процесор • Нитка викликає yield() – Нитка віддає процесор добровільно computePI() { while(TRUE) { ComputeNextDigit(); yield(); } }
  • 30. Зовнішні події • Що відбудеться, якщо нитка ніколи не буде використовувати ввід/вивід, ніколи не буде очікувати та віддавати контроль? – Можна використовувати програму ComputePI, щоб захопити всі ресурси та не віддавати процесор? » Що, якщо вона навіть нічого не пише в консоль? – У диспетчера має бути спосіб відновити контроль! • Відповідь: Використання зовнішніх подій – Переривання: обладнання посилає сигнал процесору, перериваючи поточну програму та переходячи до режиму ядра – Таймер: як будильник, який виконується кожні кілька мілісекунд • Якщо ми зробимо так, що зовнішні події виникають досить часто, ми забезпечимо, щоб диспетчер працював інколи, коли це необхідно
  • 31. Зберігання/Виклик стану («Переключення контексту») Switch(tCur,tNew) { /* Зберегти стару нитку */ TCB[tCur].regs.r7 = CPU.r7; … TCB[tCur].regs.r0 = CPU.r0; TCB[tCur].regs.sp = CPU.sp; TCB[tCur].regs.retpc = CPU.retpc;/*адреса повернення*/ /* Завантажити та виконати нову нитку */ CPU.r7 = TCB[tNew].regs.r7; … CPU.r0 = TCB[tNew].regs.r0; CPU.sp = TCB[tNew].regs.sp; CPU.retpc = TCB[tNew].regs.retpc; return; /* Return to CPU.retpc */ }
  • 32. Вибір наступної нитки • Як диспетчер вирішує, що запустити далі? – Якщо немає готових ниток – кружляє в циклі диспетчера » Альтернатива – створити нитку “idle” (бездіяльність) » Може переключити процесор в режим низького енергоспоживання – Якщо всього одна нитка - просто – Більше однієї нитки – використовувати планувальні правила • Можливі правила: – LIFO (last in/first out, останнім увійшов, першим вийшов): – Обирати випадково – FIFO (first in/first out, першим увійшов, першим вийшов): » Це «чесно» – Черга з пріоритетами: » Сортувати чергу готовності за пріоритетом (прописаним у TCB)
  • 33. Нитки режиму ядра проти ниток режиму користувача • Поки ми говорили про нитки в режимі ядра – Це ті нитки, що підтримують ядро ОС – Кожна нитка може виконуватись чи очікувати незалежно – Один процес може містити кілька ниток, що очікують на різне • Недолік ниток рівня ядра: дорого – Для планування необхідно переключатися у режим ядра • Існує полегшене рішення: Нитки рівня користувача – Користувацька програма надає планувальник та пакет роботи з нитками – Можна мати кілька ниток користувача на 1 нитку ядра – Нитки користувача можуть керуватись непримусово одна відносно іншої (переключення тільки по yield()) – Дешево • Недоліки: – Коли одна нитка ядра блокується на вводі/виводі, блокуються всі нитки рівня користувача – Ядро не враховує множину ниток рівня користувача під час планування
  • 34. Теоретичні моделі 1 до 1 Багато до 1 Багато до багатьох
  • 35. Основні простори імен FCL System System.Data System.Xml System.Web Globalization Diagnostics Configuration Collections Resources Reflection Net IO Threading Text ServiceProcess Security Design ADO SQLTypes SQL XPath XSLT Runtime InteropServices Remoting Serialization Serialization Configuration SessionState Caching Security Services Description Discovery Protocols UI HtmlControls WebControls System.Drawing Imaging Drawing2D Text Printing System.Windows.Forms Design ComponentModel
  • 36. Багатопоточність • Потік (threads) – незалежний шлях виконання, здатний виконуватися одночасно з іншими потоками • Складається з: – Ядро потоку (thread kernel) – структура даних, набір властивостей якої описує потік. Містить контекст потоку (700-1240 байт) – Блок оточення потоку ТЕВ (Thread Environment Block) – місце у пам'яті, виділене і ініціалізоване в режимі користувача, містить ланцюжок обробки виключень (4- 8 Кбайт) – Стек режиму користувача (user-mode stack) – застосо- вується для переданих у методи локальних змінних і аргументів, містить адресу, що показує, звідки почне виконання потік після того, як поточний метод поверне управління (1 Мбайт) – Стек режиму ядра (kernel-mode stack) – використо- вується, коли код застосування передає аргументи на функцію ОС, що знаходиться в режимі ядра (12-24 Кбайт) – Повідомлення про створення і завершення потоків
  • 38. Переваги багатопоточності • прискорити відгук користувацького інтерфейсу програми на дії користувача, шляхом розміщення довготривалих задач в окремі потоки • повного використання потужностей багатоядерних машин та гіперпотокових одноядерних процесорів • ізоляції одного коду від іншого
  • 39. Клас System.Threading • System.Threading – типи для створення багатопо- точних застосувань – System.Diagnostics - типи для роботи з процесами Клас Опис Interlocked Надає атомарні операції для змінних, що загальнодоступні кільком потокам Monitor Надає механізм для синхронізації доступу до об'єктів Mutex Примітив синхронізації, який також може використовуватися в міжпроцесорній синхронізації Semaphore Обмежує кількість потоків, які можуть одночасно отримувати доступ до ресурсу або пулу ресурсів Thread Створює і контролює потік, задає пріоритет і повертає статус ThreadPool Надає пул потоків, який може використовуватися для створення робочих елементів, обробки асинхронного вводу/виводу, очікування від імені інших потоків і обробки таймерівTimer Надає механізм для виконання методу в задані інтервали часу
  • 40. Члени класу System.Threading.Thread • Властивості – Name – задає зрозуміле рядкове ім’я потоку – IsBackground – вказує, чи є потік фоновим • Об'єкт Thread реалізує усі стандартні операції над нитками Метод Опис Abort Виклик даного методу завершує потік Interrupt Перериває роботу потоку, що перебуває у стані WaitSleepJoin Join Блокує викликаючий потік до завершення потоку Resume Відновлює призупинену роботу потоку Sleep Статичний метод. Блокує поточний потік на задану кількість мілісекунд Start Дозволяє планувати виконання потоку (запускати потік) Suspend Припиняє роботу потоку; якщо робота потоку вже припинена, не має ефекту
  • 41. Члени класу System.Threading.Thread (2) Делегат Опис ParameterizedThreadStart Представляє метод, що виконується в об'єкті Thread ThreadStart Представляє метод, що виконується в об'єкті Thread Перерахування Опис ThreadPriority Задає пріоритет виконання потоку Thread { Highest, AboveNormal, Normal, BelowNormal, Lowest } ThreadState Визначає набір всіх можливих станів виконання для потоків Thread
  • 42. Приклад: створення потоку на основі делегата ThreadStart
  • 43. Приклад: створення потоку на основі делегата ParameterizedThreadStart
  • 44. Активні та фонові потоки • Активні потоки або потоки переднього плану (foreground threads) виконуються завжди до свого завершення, а якщо основний потік завершив свою роботу, тоді програма очікує завершення усіх вторинних пріоритетних потоків • Фонові потоки (background threads) або потоки- демони автоматично зупиняються та знищуються після завершення усіх активних потоків • Активні та фонові потоки не є синонімами первинних та вторинних (робочих) потоків – Первинний потік – перший потік, створений точкою входу процеса – Робочий потік – додатковий вторинний потік, породжений первинним потоком (напр., функцією Thread.Start())
  • 45. Приклад: активні та фонові потоки
  • 46. Література • Рихтер Дж. CLR via C#. Програм- мирование на платформе Microsoft .NET Framework 4.0 на языке C#. 3-е изд. – СПб.: Питер, 2012. – 928 с. • Троелсен Э. Язык программирования C# 5.0 и платформа .NET 4.5. 6-е изд., - М.: Издательский дом “Вильямс”, 2013. - 1312 с. • Корисні ресурси – http://msdn.microsoft.com/ru-ru/library/
  • 47. Крос-платформне програмування Лекція 14 Багатопоточність у .NET Framework. Загальні властивості паралелізму 27 травня, 2015 Примітка: частину слайдів лекції підготовлено за матеріа- лами курсу prof. J.Kubiatowicz (CS162 ©UC Berkeley) http://inst.eecs.berkeley.edu/~cs162/fa10/
  • 48. Thread pool • Пул потоків - набір доступних застосуванням потоків – Черга задач на виконання – N ниток у замороженому стані – Щойно з’являється задача, вона призначається першій вільній нитці – Після завершення задачі нитка не знищується, а заморожується та помічається як вільна
  • 49. Thread pool (2) • Типи потоків у пулі – Робочі потоки (worker thread) – використовуються, коли застосування вимагає від пулу виконання асинхронної обчислювальної операції – Потоки введення-виведення (I/O thread) – служать для повідомлення коду про завершення асинхронної операції введення-виведення – state - параметр (дані стану) – саllback – метод, який буде викликаний потоком з пулу та повинен відповідати делегату System.Threading.WaitCallback – ThreadPool.SetMaxThreads() – загальна кількість ниток – ThreadPool.GetAvailableThreads() – кількість вільних ниток
  • 52. Переваги та обмеження пулу потоків • Переваги – Пул потоків управляє потоками ефективно, зменшуючи кількість потоків, що створюються, запускаються та зупиняються – Використовуючи пул потоків, можна зосередитися на вирішенні завдання, а не на інфраструктурі потоків застосування • Обмеження – Усі потоки у пулі є фоновими – Не можна змінювати пріоритет або ім'я потоку, що знаходиться у пулі – Потоки у пулі підходять для виконання тільки коротких завдань
  • 53. Thread vs ThreadPool • Клас Thread – Дороге створення та видалення ниток – Повний контроль над об’єктом (пріорітети, заморозка/пробудження, Thread.Abort()) • Клас ThreadPool – Дешеве створення ниток – Вбудована черга задач – Відсутність контролю над ниткою
  • 54. Класи Timer платформи .NET Framework • У .NET Framework доступно кілька класів Timer, які можуть застосовуватися для забезпечення виклику методів по закінченню певного проміжку часу Простір імен Опис System.Threading У його конструкторі класу Timer можна передавати делегат, що викликається через вказаний інтервал часу System.Timers Клас Timer є компонентом, тому що успадкований від базового класу Component. Використовує клас Timer System.Threading.Timer, але замість делегатів надає механізм на основі подійSystem.Windows.Forms Елементи керування WindowsForms прив'язуються до потоку, що їх створив. Зворотній виклик в цей потік дозволяє здійснювати клас Timer з простору імен System.Windows.FormsSystem.Web.UI Клас Timer є розширенням Ajax та призначений для використання з веб- сторінками System.Windows.Threading Клас DispatcherTimer застосовується в WPF- застосуваннях та виконується в рамках потоку для користувача інтерфейсу
  • 55. Використання таймера Один з перевантажених конструкторів таймера: Приклад виклику таймеру для виводу поточного часу:
  • 56. Приклад: використання таймера (2) • Метод Change() служить для зміни проміжку після створення об’єкту Timer
  • 57. Багатопроцесорність vs Багатозадачності • Порівняння визначень: – Багатопроцесорність ≡ Багато CPU – Багатозадачність ≡ Багато задач чи процесів – Багатонитковість ≡ Багато ниток на 1 процес • Що означає, що дві нитки виконуються “одночасно”? – Планувальник в ОС може обрати будь-який механізм виконання та переключення: FIFO, Random, … – Диспетчер може вирішувати, виконувати нитку до завершення чи розбити її на великі чи малі частини A B C BA ACB C BБагатозадачність A B C Багатопроцесорність
  • 58. Приклад: спільно використовувані ресурси Очікуваний результат: Отриманий результат:
  • 59. Коректність систем з нитками • Диспетчер може розмістити у часі нитки як завгодно, аби програма працювала правильно – Чи можна протестувати програму на коректність? – Як дізнатись, що програма працює? • Незалежні нитки: – Нитки не мають нічого спільного – Детермінованість ⇒ Вхідні дані визначають результат – Відтворюваність ⇒ Можна повторити стартові умови, ввід/вивід – Порядок виконання ниток не має значення (якщо працює switch()) • Взаємодія ниток: – Нитки мають спільні дані – Недетермінованість – Невідтворюваність • Недетермінованість та невідтворюваність означає, що помилки виникають не завжди та випадково – Інколи називають “Heisenbugs” (хайзенбагз)
  • 60. Навіщо ниткам взаємодіяти • Люди взаємодіють; комп'ютери нам допомагають, тому вони мають взаємодіяти – За аналогією, неповторність та недетермінізм роботи людей – відома проблема «точних планів» • Перевага 1: Загальні ресурси – Один комп'ютер, багато користувачів – Один рахунок, багато банкоматів (ATM) » Що, якби ATM-и обновлялись тільки уночі? – Вбудовані системи (керування роботом: координувати руку та долоню) • Перевага 2: Прискорення – Сумістити ввід/вивід та обчислення » Багато файлових систем зчитують дані на перед – Багатопроцесорність – розбити програму на паралельні частини • Перевага 3: Модульність – Важливіше, ніж здається на перший погляд – Розділити велику програму на маленькі частини » Під час компіляції в UNIX gcc викл. «cpp | cc1 | cc2 | as | ld» » Гнучкість під час розширення системи
  • 61. Проблема знаходиться на низькому рівні • Коли нитки працюють з незалежними даними, порядок виконання не має значення Thread A Thread B x = 1; y = 2; • Але коли (спочатку y = 12) Thread A Thread B x = 1; y = 2; x = y+1; y = y*2; – Які можливі значення x? • Або, які можливі значення x з наступного прикладу? Thread A Thread B x = 1; x = 2; – X може бути 1 або 2 (не детерміновано!) – Може бути навіть 3, якщо робота з пам'яттю побітова » Нитка A пише 0001, B пише 0010. » Порядок запису ABABABBA дає 3!
  • 62. • Багатониткові програми мають працювати коректно при за будь-якої послідовності виконання ниток – Нитки що взаємодіють створюють недетермінованість і неповторюваність – Важко відлагоджувати, якщо погано спроектовано! • Приклад: Therac-25 – Інструмент радіаційної терапії » Програмний контроль прискорювача » Програмний контроль дозування – Помилка в програмі призвела до смерті кількох пациентів » Рейсові умови виникали через погане проектування » “Було визначено, що швидкість вводу даних під час редагування була ключовим фактором виникнення помилкової ситуації: передозування виникало, якщо дані вводилися надто швидко.” » http://dl.dropbox.com/u/14787239/lec/add/TheracNew.pdf Умови коректності
  • 63. Запуск першого космічного шатла • Запуск першого шатлу було перервано за 20 хвилин перед запланованим стартом • У шатлі було 5 комп'ютерів: – 4 виконували “Primary Avionics Software System” (PASS) » Асинхронна в реальному часі » Керування усіма системами » Результати синхронізувались і порівнювались кожні 3 - 4 мс – П'ятий комп'ютер був “Backup Flight System” (BFS) » синхронізується з PASS за необхідністю » запрограмований іншою командою розробників • Запуск припинено, тому що BFS і PASS не співпали – Існував 1/67 шанс, що PASS вийде з синхронізації – Помилка в ініціюючому коді PASS » Запит на відкладену ініціалізацію був покладений в чергу таймера » В результаті черга таймера не була пустою в очікуваний час – Помилка не була знайдена при ретельному моделюванні – http://dl.dropbox.com/u/14787239/lec/add/garman_bug_81.pdf PASS BFS
  • 64. Синхронізація • Синхронізація між потоками одного процесу – оператор lock – клас Monitor – клас Interlocked • Синхронізація між потоками різних процесів – клас Mutex – клас Semaphore – клас Event – клас ReaderWriterLockSlim
  • 65. Оператор lock Синхронізуючий об’єкт або маркер обов’язково повинен мати тип-посилання Блок синхронізації • Не дозволяє жодному потоку увійти у блок синхро- нізації, коли в ньому знаходиться інший потік – При спробі входу іншого потоку в заблокований код буде потрібно дочекатися зняття блокування об'єкта – Маркер використовується для визначення області блокування (рекомендується уникати блокування типу public або екземплярів, якими код не керує)
  • 67. Клас Monitor Запис lock (obj) { A++; } перетворюється компілятором у такий код: Monitor.Enter(obj); try { A++; } finally { Monitor.Exit(obj); } синхронізований регіон для obj синхронізований регіон для obj блокування об’єкта obj ; позначає початок критичної секції звільнення об’єкта obj
  • 68. Методи класу Monitor • Методи Wait(), Pulse() та PulseAll() можуть викликатися тільки з заблокованого фрагмента блоку • Можливі форми методу Wait() Метод Опис Wait() Звільняє блокування об’єкта для того, щоб дозволити іншим потокам здійснити блокування та отримати доступ до об’єкта. Повертає булеве значення, яке вказує на успіх очікування чи завершення його по закінченню таймаутаPulse() Посилає сигнал потоку, що стоїть першим у черзі очікування. Очікуючий потік переміщується в чергу готових та може отримати блокування для об’єкта PulseAll() Посилається сигнал всім потокам, і вони переміщуються в чергу готових TryEnter() Робить спробу отримати доступ до об’єкта протягом часу, вказаного у вхідних параметрах. Якщо доступ отримано, то метод повертає true, якщо ні – false
  • 69. Прикад: використання методів класу Monitor • У порівнянні з попе- редніми прикладами час виконання цієї програми буде на декілька порядків більшим
  • 71. Клас Interlocked Метод Опис int newV Interlocked.Increment(ref intV); Збільшує значення змінної на одиницю та повертає ще й на додаток нове значення int newV Interlocked.Decrement(ref intV); Зменшує значення змінної на одиницю double newA = Interlocked.Exchange(ref doubA, 90.5); Присвоює змінній вказане значення та повертає початкове значення Interlocked.CompareExchange(ref i, 99, 83); Якщо значення i рівне 83, тоді замінити його на 99 • Надає атомарні операції для змінних, загально- доступних декільком потокам • Є набагато більш швидким підходом у порівнянні з іншими підходами щодо забезпечення синхронізації
  • 73. Паралельна система • Паралельна програма невід’ємна від паралельної архітектури • Паралельна система – це паралельна програма + паралельна архітектура – Час виконання T залежить від » Кількості процесорів (N) » Розміру задачі (n) – Головна умова коректності » Результат має бути детермінованим » Результат не має залежати від кількості процесорів
  • 74. Паралельна система як чорна скриня • Розглянемо приклади виконання паралельних програм • Час виконання в залежності від кількості 0 200 400 600 800 1000 1200 0 5 10 15 20 25 30 35 Job1 0 200 400 600 800 1000 1200 0 5 10 15 20 25 30 35 Job2
  • 75. Головні властивості • Основні показники якості паралельної програми » T1 – час на одному процесорі, TN – час на N процесорах • Параметри прискорення і ефективність - суперечать – Збільшуючи N, збільшуємо прискорення S, але зменшуємо ефективність – Ідеальна ефективність, E=1, досягається при N=1 • Властивість – Ідеальне прискорення S=N » Іноді буває надлінійне прискорення (S>N) (в цьому курсі не розглядаємо) Прискорення: Ефективність: N N T T S 1 = N N N NT T N S E 1 ==
  • 76. Повертаючись до початкового прикладу Прискорення Ефективність • Висновки: – Загально погана якість паралельних програм – Друга програма трохи краща 0 1 2 3 4 5 6 7 8 9 10 0 5 10 15 20 25 30 35 Job1 Job2 0 0,2 0,4 0,6 0,8 1 1,2 0 5 10 15 20 25 30 35 Job1 Job2
  • 77. Послідовна частина • Нехай – η - частина алгоритму, яка може бути розпаралелена – σ - частина алгоритму, яка не може бути розпаралелена (послідовна частка) • Тоді час виконання паралельної програми • Прискорення буде мати вигляд 1T N TN       += η σ ( ) ( ) NNTN T T T S N N /)1( 1 /)1( )1( / 1 11 σσσσ σσ ησ ησ −+ = −+ −+ = + + ==
  • 78. Обмеження паралелізму • Закон Амдала (1967) «Прискорення обмежене послідовною частиною» – Нехай σ – послідовна частка ( ) 26.102095.05.01%5,20 =+=⇒== NSN σ N SN )1( 1 σσ −+ ≤ σ 1 lim ≤ ∞→ N N S Приклад:
  • 79. Критика закону Амдала • Закон Амдала припускає, що послідовна частина в програмі є постійною – Але вона часто залежить від розміру задачі! » Зазвичай, чим більша задача, тим менша частка, яку не можна розпаралелити » Наприклад, припустимо, ми моделюємо погодні умови • Якщо у нас буде більший паралельний комп'ютер, ми захочемо збільшити задачу, щоб отримати точніші результати • Закон Амдала сильно песимістичний! – Насправді це уповільнило розвиток паралельних систем у 1960-70х роках
  • 80. Масштабована послідовна частина • Нехай n – розмір задачі. Тоді послідовна та паралельні частини σs і η будуть мати вигляд: σs (n) + η(n) = 1 • Час виконання послідовної версії програми буде T(1) = σs (n)T(N) + η(n)NT(N) • Тоді прискорення буде мати вигляд S(N) = σs (n)+η(n)N = N-σs (n)(N-1) • Таке прискорення зветься масштабованим Якщо n прямує до нескінченності, σs (n) прямує до 0, а S прямує до N.
  • 81. Реабілітація паралелізму • Закон Густавсона-Барсиса (1988) – «Збільшуючи розміри задачі, збільшуємо паралельну частку, досягаємо необхідного прискорення»: • де σs - масштабована послідовна частка • Наприклад: 1) При N=10 послідовна частка склала усього 3% загального часу 2) Для того, щоб досягнути прискорення в 7 разів при N=8 треба збільшити розмір задачі s так, щоб послідовна частка дорівнювала: ( ) s NN NNS σ⋅−−≤ 1 ( ) ( ) ( ) ( ) 14.018781 =−−=−−≤ NSN N s Nσ ( ) 73.903.011010 =⋅−+=⇒ NS
  • 82. Порівняння законів Амдала і Густавсона-Барсиса • Закон Амдала: “Якщо ви вже проїхали 50 км зі швидкістю 50 км/год, ви не можете проїхати 100 км з середньою швидкістю, більшою, ніж 100 км/год” • Закон Густавсона-Барсиса: “Якщо можна обирати відстань, то незалежно від того, скільки часу ми їхали і з якою швидкістю, ми можемо досягти будь-якої бажаної середньої швидкості” 0 50 км 100 км 0 50 км ? км
  • 83. Критика закону Густавсона-Барсиса • Припускає, що для будь-якої задачі можна знайти дані великого розміру – Але для багатьох задач це не так • Для задач нелінійної складності збільшення розмірів задачі не дає такого суттєвого збільшення паралелізму – Наприклад, для задач складністю O(n3 ) збільшення кількості операцій удвічі збільшує розмір задачі лише на 26% • Закон Густавсона-Барсиса сильно оптимістичний! – І це сприяло розвитку паралельних систем у 1970-80х роках
  • 84. Реальна картина • Для будь-якої паралельної задачі з обмінами виникають комунікаційні витрати – вони збільшуються разом з N • Тому існує таке N, на якому досягається оптимальне прискорення (для фіксованого розміру задачі!) Графік прогнозованого та експериментального прискорення
  • 85. Навіщо? • Розв’язувати задачі швидше – (це очевидна мета) Ще? • Розв’язувати великі задачі – Збільшення деталізації – Підвищення точності • Витримати велике навантаження – Розподілити вхідний потік запитів – Розподілити місце збереження даних • Використання віддалених ресурсів – Використання усіх доступних вільних ресурсів (ГРІД)
  • 86. Закон Мура – тренд розвитку IT-індустрії 2X транзисторів на чипі кожні 1.5 років Закон Мура Гордон Мур (співзасновник Intel) спрогнозував в 1965 році, що кількість транзисторів на чипі буде подвоюватись кожні 18 місяців. Мікропроцесори стають меншими, щільнішими та потужнішими.
  • 87. Сприйняття закону Мура у 60-х роках • У 1960-х роках закон Мура виглядав безглуздо, тому що прогнозував через сорок років “маленькі домашні комп’ютери”
  • 88. Закон Мура (с) UC Berkeley CS10 Beauty and Joy of Computing
  • 89. На попередній лекції: багатоядерні процесори • “БАГАТО-ядерні” процесори – 64? 128? Важко спрогнозувати границю • Як їх застосувати? – Нехай 2 ядра - на відео і аудіо – Нехай 1 ядро - на текстовий редактор, 1 - на браузер – 76 – на захист від вірусів??? • Треба застосувати паралелізм на усіх рівнях • Intel 80-ядерний чип (02.2007) – 80 простих ядер – 2 АЛУ / ядро – Решіткова мережа – 100 мільйонів транзисторів – 65nm технології • Intel Single-Chip Cloud Computer (04.2010) – 24 “плитки” по 2 ядра – 24-клітинна решіткова мережа – 4 DDR3 контролери пам’яті – Апаратна підтримка обміну повідомленнями (с) John Kubiatowicz Adapted from UCB
  • 90. Що робити з оцим?
  • 92. Застосування: обробка великого потоку звертань • Веб-сервери та пошукові системи • Фінансові системи • Зберігання та обробка даних великого обсягу • Cloud
  • 93. Застосування паралельних систем: 3D-анімація • 1. Якість та роздільна здатність понад усе (3D-мультиплікація) • 2. Візуалізація у реальному часі (30 fps) (3D-ігри) Моделювання Анімація Освітлення Візуалізація web.engr.oregonstate.edu/~mjb/intro2009/
  • 94. Застосування: моделювання фізичних процесів • Кінцево-різницеві та кінцево-елементні – Погода – Медицина – Будівництво – Сейсміка – Гідродинаміка – Аеродинаміка • Взаємодія часток – Молекулярна динаміка – Моделювання всесвіту
  • 95. Література • Рихтер Дж. CLR via C#. Програм- мирование на платформе Microsoft .NET Framework 4.0 на языке C#. 3-е изд. – СПб.: Питер, 2012. – 928 с. • Троелсен Э. Язык программирования C# 5.0 и платформа .NET 4.5. 6-е изд., - М.: Издательский дом “Вильямс”, 2013. - 1312 с. • Корисні ресурси – http://msdn.microsoft.com/ru-ru/library/
  • 97. На апаратному рівні паралелізм усюди! • Сучасні процесори складаються приблизно з 109 транзисторів – Очевидно, що вони мають працювати паралельно – Наскільки програмісту треба це контролювати? • Як однопроцесорні архітектури виділяють паралелізм? – Автоматично: шукають у потоці команд ILP – Instruction Level Parallelism • До 2002 року задача архітектури комп'ютера – приховати паралелізм від програміста, компілятора, ОС • Приклади ILP технологій: – Конвеєрне виконання: одночасне виконання різних частин інструкції – Суперскалярна архітектура: виконувати >1 інструкції за такт – VLIW: Вказати паралелізм прямо в інструкції – Векторні операції: вказувати групу незалежних операцій – Невпорядковане виконання інструкцій (OOO, Out of order execution)
  • 98. Засоби розробки для паралельних систем • OpenMP – для багатоядерних систем – (та БАГАТОядерних систем) • MPI – переважно систем з розподіленою пам'яттю – Але є перспективи використання для будь-яких систем – “MPI – assembly language of parallel programming” • OpenCL/CUDA – для графічних прискорювачів – Яка з цих двох технологій переможе – зараз не зрозуміло – Але є потенційна можливість використання OpenMP • ГРІД? – розподілені обчислення, поки що не мають домінуючої технології
  • 99. Інструменти паралельного програмування • Мови паралельного програмування – Складно створити та розвивати – Потребує перенавчання » І переписати усі програми – Приклади: Occam, CHILL • Розширення послідовних мов – Середня складність створення – Вивчати треба тільки особливості мови – Приклади: Brook, CUDA, OpenCL • Розширення компіляторів – Вивчати треба тільки паралельні конструкції » Треба чітка стандартизація специфікацій – Приклад: OpenMP • Бібліотеки та інтерфейси (API) – Вивчати треба тільки API – Паралелізм треба описувати у явному вигляді » Але це складно – Приклади: PVM(1989), MPI (1994)

Notes de l'éditeur

  1. Like warden in an insane asylum. Put patients in locked padded cell – patient can bang head against wall, but otherwise can’t cause any problems. Pascal – like putting them in a straight jacket. C is like giving them knives to play with.
  2. Emergency crash of operating system called “panic()”
  3. OS’s have almost human characteristics – unpredictable, hard to understand, … Different things share the same CPU – one thread, then another Similar to schizophrenia, like the movie Sybil, one body shared by several people, say we start with Dave Patterson Threads are like the personalities of the CPU. First one thread/personality uses the CPU, then another,…
  4. Yield is for really nice people – Ever see two people at the supermarket checkout line? You first, no you first, …
  5. Patterson’s a nice guy, so he gives up the body after using it for awhile and let’s John Kubitowicz have it. But Kubi’s not so nice, so he won’t give up control… If you want to wake up for a final, you set your clock, or ask your roommate to pour water over your head – OS does the same
  6. Like book organization in a library
  7. Потік (threads) – незалежний шлях виконання, здатний виконуватися одночасно з іншими потоками Складається з: Ядро потоку (thread kernel) – для кожного створеного в ній потоку ОС виділяє і ініціалізує одну зі структур даних. Набір властивостей цієї структури описує потік. Структура містить також так званий контекст потоку, тобто блок пам&amp;apos;яті з набором регістрів процесора Блок оточення потоку ТЕВ (Thread Environment Block) – це місце в пам&amp;apos;яті, виділене і ініціалізоване в режимі користувача (адресний простір, до якого має швидкий доступ код застосувань). Займає одну сторінку пам&amp;apos;яті Стек режиму користувача (user-mode stack) – застосовується для переданих в методи локальних змінних і аргументів. Також містить адресу, що показує, звідки почне виконання потік після того, як поточний метод поверне управління Стек режиму ядра (kernel-mode stack) – використовується, коли код застосування передає аргументи на функцію ОС, що знаходиться в режимі ядра Повідомлення про створення і завершення потоків
  8. Послідовність дій при створенні додаткових потоків: 1. Створити статичний чи об’єктний метод, який має служити вхідною точкою для нового потоку та відповідає сигнатурі делегата ThreadStart чи ParameterizedThreadStart: public delegate void ThreadStart(); public delegate void ParameterizedThreadStart(Object obj); 2. Створити об’єкт відповідного делегата та передати його конструктору ім’я методу, що є вхідною точкою для нового потоку 3. Створити об’єкт Thread та передати його конструктору цей делегат. 4. Задати початкові параметри потоку (ім’я, рівень пріоритету тощо). 5. Запустити потік на виконання (метод Start()).
  9. Створення додаткового потоку з використанням делегата ThreadStart підходить для тих випадків, коли для роботи додаткового потоку немає потреби передавати які-небуть аргументи з основного потоку. За необхідності передачі додатковому потоку певної інформації використовують делегат ParameterizedThreadStart. Вхідні дані оформлюються у вигляді певного об’єкту, тобто необхідно попередньо визначати структуру додаткового класу, що міститиме перелік усіх необхідних даних.
  10. У CLR усі потоки поділяються на активні (foгeground) і фонові (фон). При завершенні активних потоків в процесі CLR примусово завершує також всі запущені на цей момент фонові потоки. При цьому завершення фонових потоків відбувається негайно і без вкидання виключень. Отже, активні потоки має сенс використовувати для виконання завдань, які обов&amp;apos;язково потрібно завершити, наприклад, для переміщення на диск даних з буфера обміну. Фонові ж потоки можна залишити для таких некритичних речей, як перерахунок осередків електронних таблиць або індексування записів. Адже ця робота може бути продовжена і після перезавантаження додатки, а значить, немає необхідності насильно залишати додаток працювати, коли користувач намагається його закрити. Ввести в CLR концепцію активних і фонових потоків знадобилося для кращої підтримки доменів застосувань. Як ви знаєте, в кожному домені може бути запущено окремем застосування, при цьому кожне таке застосування може мати власний фоновий потік. Навіть якщо одне з застосувань завершується, змушуючи завершитися свій фоновий потік, середа CLR все одно повинна функціонувати, підтримуючи інші програми. І тільки після того як всі застосування з усіма своїми фоновими процесами будуть завершені, можна буде знищити весь процес.
  11. X could be (13, 5, 3)
  12. Розглянемо приклад програми, що імітує цокання годинника і відображає цей процес на екрані словами &amp;quot;тік&amp;quot; і &amp;quot;так&amp;quot;. Для цієї мети в програмі створюється клас TickTock, що містить два наступних методи: Tick() і Tock(). Метод Tick() виводить на екран слово &amp;quot;тік&amp;quot;, а метод Tock() – слово &amp;quot;так&amp;quot;. Для запуску годинника далі в програмі створюються два потоки: один з них викликає метод Tick(), а інший – метод Tock(). Переслідувана у даному випадку мета полягає в тому, щоб обидва потоки виконувалися, по черзі виводячи на екран слова &amp;quot;тік&amp;quot; і &amp;quot;так&amp;quot;, з яких утворюється повторюваний ряд &amp;quot;тік-так&amp;quot;, що імітує хід годинника. Розглянемо цю програму більш докладно. У методі Main() створюється об&amp;apos;єкт tt типу TickTock(), який використовується для запуску двох потоків на виконання. Якщо у методі Run() з класу MyThread виявляється ім&amp;apos;я потоку Tick, що відповідає ходу годинника &amp;quot;тік&amp;quot;, то викликається метод Tick(). А якщо це ім&amp;apos;я потоку Tock(), що відповідає ходу годинника &amp;quot;так&amp;quot;, то викликається метод Tock(). Кожен з цих методів викликається п&amp;apos;ять разів поспіль з передачею логічного значення true в якості аргументу. Годинник іде до тих пір, поки цим методам передається логічне значення true, і зупиняється, як тільки передається логічне значення false. Найважливіша частина розглянутої програми знаходиться в методах Tick() і Тоск(). Перш за все слід звернути увагу на код методу Tick() у блоці lock. Нагадаємо, що методи Wait() і Pulse() можуть використовуватися тільки в синхронізованих блоках коду. На початку методу Tick() перевіряється значення поточного параметру, яке служить явною ознакою зупинки годинника. Якщо це логічне значення false, то годинник зупинено. У цьому випадку викликається метод Pulse(), який дозволяє виконання будь-якого потоку, що очікує своєї черги. Якщо ж годинник йде при виконанні методу Tick(), то на екран виводиться слово &amp;quot;тік&amp;quot; з пробілом, потім викликається метод Pulse(), а після нього – метод Wait(). При виклику методу Pulse() дозволяється виконання потоку для того ж самого об&amp;apos;єкта, а при виклику методу Wait() виконання методу Tick() припиняється до тих пір, поки метод Pulse()не буде викликаний з іншого потоку. Таким чином, коли викликається метод Tick(), відображається одне слово &amp;quot;тік&amp;quot; з пробілом, дозволяється виконання іншого потоку, а потім виконання даного методу призупиняється.