344.50K
Категория: ИнформатикаИнформатика

Технології програмування КС (лекція 7)

1.

Технології програмування КС
лекція 7 (частина 2)
Проф. Цимбал О.М., кафедра ТАВР,
ХНУРЕ, Харків, Україна

2.

Багатозадачність. Процеси та потоки
Багатозадачність є необхідною рисою операційних систем. Справжня багатозадачність
залишається можливою тільки у разі паралельного виконання декількох обчислювальних
операцій. На комп’ютерах з одним ЦП це неможливо, але можливо, якщо в наявності є
багато процесорів, або багато ядер одного процесора.
Але й те, що центральний процесор під орудою операційної системи має змогу планувати
виконання декількох обчислювальних операцій у певній послідовності є суттєвим
здобутком і є однією з форм багатозадачності, яка забезпечується операційною системою.
Microsoft Windows має два типи багатозадачності.
В основі першого типу – поняття процесу (process), другого – поняття потоку (thread).
Процесом є окрема програма, що виконується операційною системою. У багатозадачності
такого типу дві або більше програм можуть виконуватися одночасно.
Потік є частиною процесу, що виконується. Кожен процес складається щонайменше з
одного потоку, але може містити два чи більше потоків. За потокової багатозадачності
декілька частин однієї програми можуть виконуватися одночасно.
Потокова багатозадачність підтримується Windows починаючи з W’95 та W’NT.
Потокова багатозадачність надає змогу розробляти програми шляхом розділення їх на
окремі виконувані блоки і керування ходом виконання усієї програми в цілому.
Керування роботою потоків забезпечується засобами синхронізації.

3.

Багатозадачність. Процеси та потоки
Усі процеси складаються щонайменше із одного потоку
виконання – головного потоку. Після виклику головного
потоку, до нього можна під’єднати виконання додаткових
потоків. При цьому і головний і додаткові потоки
виконуватимуться не послідовно (як функції програми), а
паралельно. Це і є приклад потоковох багатозадачності.
У MFC визначаються два типи потоків: інтерфейсні і робочі.
Інтерфейсні потоки є здатними приймати та обробляти
повідомлення.
Проста MFC-програма починається з оголошення об’єкта
класу CApp (похідного від CWinApp). Це і є початок
головного потоку програми.
Клас вікна, у свою чергу, містить оголошення карти
повідомлень і відповідає за функціонування інтерфейсу
користувача. Таким чином головний потік MFC програми є
інтерфейсним потоком.

4.

Потоки та їх властивості
Робочі потоки, на відміну від інтерфейсних, не приймають і не обробляють повідомлень.
Вони спрямовуються на забезпечення виконання додаткових шляхів виконання окремих
завдань інтерфейсного потоку.
На рівні WinAPI немає різниці між робочими і інтерфейсними потоками.
У MFC потокова багатозадачність реалізується за допомогою класу CWinThread.
Згадаємо, що клас CWinApp породжується від CWinThread, тобто інтерфейсні потоки є лише
різновидом потоків, а робочі потоки є більш узагальненим поняттям.
Використання багатопотокового режиму роботи програми потребуватиме включення
заголовкового файла “afxmt.h” (mt – розшифровується як multithreading (багатопотоковість).
Створення робочого потоку здійснюється у досить простий спосіб: необхідно оголосити та
реалізувати функцію керування потоком (потокову функцію) і здійснити її запуск.
Під час створення робочого потоку найчастіше немає потреби оголошувати спеціальний,
похідний від CWinThread клас. Запуск потоку забезпечується WinAPI-функцією
AfxBeginThread().

5.

Потоки та їх властивості
AfxBeginThread() має дві перевантажених версії прототипів: одну для забезпечення запуску
робочих потоків і одну – для інтерфейсних потоків:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0,
DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
Параметрами цих прототипів є:
pfnThreadProc – покажчик на потокову функцію робочого потоку, не дорівнює NULL.
pThreadClass – покажчик на об’єкт динамічно створюваного класу (RUNTIME_CLASS),
похідного від CWinThread;
pParam – покажчик на параметр, що передається у потокову функцію pfnThreadProc;
nPriority – бажаний рівень пріоритету потоку, значення 0 означатиме такий самий рівень
пріоритету, що й у батьківського потоку, з якого здійснюється виклик створюваного потоку
(рівень пріоритету потоку встановлюється за допомогою функції SetThreadPriority());

6.

Потоки та їх властивості
nStackSize – визначає розмір стеку пам’яті нового потоку у байтах, значення 0 встановлює
такий самий розмір потоку, що й у батьківського потоку;
dwCreateFlags – прапорець режиму створення потоку, має два значення: CREATE_SUSPENDED
– потік запускається припиненим (до його поновлення функцією ResumeThread()), 0 – потік
негайно запускається (значення за замовчуванням);
lpSecurityAttrs – покажчик на структуру SECURITY_ATTRIBUTES, що визначає атрибути безпеки
потоку, NULL означає, що даний потік успадковує атрибути захисту батьківського потоку.
Підсумовуючи, можна сказати, що функція AfxBeginThread() створює новий об’єкт типу
CWinThread, викликає функцію CreateThread() для виконання потоку і, насамкінець, повертає
покажчик на потік.
Потокова функція має оголошуватися у такий спосіб:
UINT MyControllingFunction (LPVOID pParam);
Зазвичай, потік самостійно закінчує свою роботу, хоча закінчення потоку з його середини
можна викликати функцію : void AfxEndThread (UINT nExitCode);
// nExitCode визначає код завершення потоку

7.

Приклад програми з одним потоком
Послідовність побудови:
1. Оголосити класи прикладки (у звичайний спосіб)
та головного вікна програми відповідно до такого коду:
class CMain : public CFrameWnd
{public: CBrush bkBrush;
CBitmap bmp;
CDC memDC;
int m_X,m_Y;
void OnExit();
void OnPaint();
void OnLButtonDown(UINT Flags,CPoint Loc);
CMain();
DECLARE_MESSAGE_MAP()
};
2. оголосити глобальним прототип потокової функції:
UINT WorkerThread(LPVOID WinObjPtr);
3. в обробнику миші викликати робочий потік:
void CMain::OnLButtonDown(UINT flags,CPoint Loc)
{m_X=Loc.x; m_Y=Loc.y;
AfxBeginThread(WorkerThread, this);
}
4. реалізувати конструктор головного вікна із
створенням об’єктів віртуального вікна:
CMain::CMain()
{Create(NULL,"Приклад із одним потоком");
m_X=m_Y=0;
maxX=GetSystemMetrics(SM_CXSCREEN);
maxY=GetSystemMetrics(SM_CYSCREEN);
CClientDC DC(this);
memDC.CreateCompatibleDC(&DC);
bmp.CreateCompatibleBitmap(&DC,maxX,maxY);
memDC.SelectObject(&bmp);
bkBrush.CreateStockObject(WHITE_BRUSH);
memDC.SelectObject(&bkBrush);
memDC.PatBlt(0,0,maxX,maxY,PATCOPY);
}

8.

Приклад програми з одним потоком
5. реалізувати код робочого потоку:
UINT WorkerThread(LPVOID WinObjPtr)
{int i;
TEXTMETRIC tm; // структурf параметрів тексту
char str[255];
CMain *ptr=(CMain*) WinObjPtr;
// приведення
параметра WinObjPtr до типу CMain
ptr->memDC.GetTextMetrics(&tm);
for(i=0; i<10; i++)
{Sleep(500); // затримка у 500 мілісекунд
wsprintf(str,"Робочий потік: сигнал № %d",i);
ptr->memDC.TextOut(ptr->m_X, ptr->m_Y, str, strlen(str));
ptr->m_Y=ptr->m_Y+tm.tmHeight+
tm.tmExternalLeading;
ptr->InvalidateRect(NULL);
MessageBeep(MB_OK);
}
ptr->MessageBox("Кінець роботи потоку","End");
return 0;
}

9.

Приклад програми з двома потоками
1. Оголоcити потокові функції:
UINT FirstThread(LPVOID WinObjPtr);
UINT SecondThread(LPVOID WinObjPtr);
UINT SecondThread(LPVOID WinObjPtr)
{char a[100]; CClientDC dc(ptr);
CMainWin *ptr=(CMainWin*)WinObjPtr;
for(int i=0;i<25;i++)
2. Реалізувати виклик потокових функцій:
void CMainWin::OnLButtonDown(UINT flags, CPoint Loc) {Sleep(750); wsprintf(a,"Second thread execution: %d",i);
dc.TextOut(200,i*15,a,strlen(a));}
{
AfxBeginThread(FirstThread,this);
ptr->MessageBox("End of Thread_2“,"Thread_2", MB_ICONHAND);
AfxBeginThread(SecondThread,this);
return 0;
}
}
3. Реалізувати потокові функції:
UINT FirstThread(LPVOID WinObjPtr)
{char a[100];
CMainWin *ptr=(CMainWin*)WinObjPtr;
CClientDC dc(ptr);
for(int i=0;i<20;i++)
{Sleep(500);
wsprintf(a,"First thread execution: %d",i);
dc.TextOut(10,i*15,a,strlen(a));
}
ptr->MessageBox("End of Thread_1",
"Thread_1",MB_ICONHAND);
return 0;
}

10.

Керування рівнем пріоритету процеса
Кожен процес та потік має певні характеристики пріоритету. Ці характеристики визначають рівень
першочерговості виконання процесу або потоку у порівнянні з іншими процесами та потоками, що
одночасно виконуються операційною системою.
Microsoft Windows (як і інші ОС)
має спеціальну системну
програму – Task Manager, яка
відображає стан програм, що
запущені користувачем або
автоматично використовуються
операційною системою. Task
Manager дозволяє контролювати стан завантаженості процесора, взаємодію з локальною
мережею, стан користувачів
системи тощо. Серед
можливостей Task Manager –
контролювання рівня
пріоритету процесів

11.

Керування рівнем пріоритету процесів
Аналогічно до процесів, кожен потік має свої власні рівні пріоритетів. Рівень пріоритету є комбінацією двох
значень: значення класу пріоритету процесу і значення пріоритету потоку, відносно пріоритету процесу.
Реальний пріоритет потоку є сумою пріоритету класу процесу і рівня пріоритету потоку. Пріоритет
процесу вказує, скільки робочого часу процесора має витрачатися на його потреби (чим більший
пріоритет – тим більше часу має витрачатися, чим менший – тим менше часу).
За допомогою спеціальних функцій WinAPI можна отримати інформацію про клас пріоритету процесу і
встановити нове значення класу пріоритету:
DWORD GetPriorityClass(HANDLE hProcess);
BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);
де hProcess – дескриптор процесу, а dwPriorityClass – значення класу пріоритету процесу.
Значення
HIGH_PRIORITY_CLASS
IDLE_PRIORITY_CLASS
NORMAL_PRIORITY_CLASS
REALTIME_PRIORITY_CLASS
Коментар
Встановлюється для процесів, виконання яких є критичним в часі і має здійснюватися першочергово у
порівнянні з процесами із значеннями пріоритету NORMAL або IDLE. Приклад – Windows Task
Manager. Слід обережно використовувати цей рівень, бо його використання займатиме основні
ресурси центрального процесора системи.
Використовується в разі простою системи. Процес такого рівня може перериватися будь-яким іншим
процесом більш високого рівня пріоритету. Прикладом є програми – зберігачі екрана (screen savers).
Рівень пріоритету за замовчуванням. Характеризує процеси без спеціальних вимог.
Застосовується для процесу, що має найвищий у системі пріоритет і здатний переривати роботу усіх
інших процесів, включно із процесами операційної системи. Забезпечує захоплення усіх ресурсів
центрального процесора.

12.

Керування рівнем пріоритету процесів та потоків
Додаткові рівні пріорітетів:
BELOW_NORMAL_PRIORITY_CLASS (нижчий від нормального)
та ABOVE_NORMAL_PRIORITY_CLASS (вищий за нормальний).
Значення пріоритету процесу можна отримати, наприклад, додавши у функцію InitInstance() такі рядки:
HANDLE proc=GetCurrentProcess();
// отримання дескриптора процесу
SetPriorityClass(proc, IDLE_PRIORITY_CLASS); // встановлення рівня пріоритету
Потік, як частина процесу, має своє значення пріоритету. Пріоритет потоку означає скільки часу
центрального процесора системи займається у загальному часі процесу. За замовчуванням, усі потоки
створюються із нормальним (THREAD_PRIORITY_NORMAL) рівнем пріоритету.
Пріоритет потоку може бути встановлено або отримано за допомогою функцій класу CWinThread:
BOOL CWinThread :: SetThreadPriority (int nPriority);
// встановлює пріоритет
int CWinThread :: GetThreadPriority ();
// повертає значення пріоритету
де nPriority – необхідний рівень пріоритету
BOOL CApp::InitInstance(void)
{HANDLE proc=GetCurrentProcess(); // дескриптор
процесу
SetPriorityClass(proc,
ABOVE_NORMAL_PRIORITY_CLASS);
m_pMainWnd = new CMainWin;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}

13.

Керування рівнем пріоритету потоків
Рівні пріоритетів потоків:
THREAD_PRIORITY_TIME_CRITICAL
// найвищий рівень
THREAD_PRIORITY_HIGHEST
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_NORMAL
THREAD_PRIORITY_BELOW_NORMAL
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_IDLE
// найнижчий рівень
Зміст значень, перелічених від найвищого пріоритету до найнижчого, легко зрозуміти. Встановлення
пріоритетів потоків також встановлюється WinAPI функціями SetThreadPriority() та GetThreadPriority().
Зазначимо, що WinAPI надає ширші можливості у встановленні пріоритетів. Зокрема, MSDN дає інформацію про 31 рівень пріоритетів потоків та процесів.
Функція AfxGetThread() повертає дескриптор потоку, з якого викликана. Її можна використати і під
час встановлення пріоритету потоку:
SetThreadPriority(AfxGetThread(), THREAD_PRIORITY_ABOVE_NORMAL);
Нагадаємо, що рівень пріоритету потоку можна встановити при його створенні, наприклад:
AfxBeginThread(WorkerThread, this, THREAD_PRIORITY_ABOVE_NORMAL);
English     Русский Правила