на тему рефераты Информационно-образоательный портал
Рефераты, курсовые, дипломы, научные работы,
на тему рефераты
на тему рефераты
МЕНЮ|
на тему рефераты
поиск
Особливості багатозадачності в середовищі Windows
p align="left">Отже, основною проблемою стала неможливість коректного зняття з виконання графічного потоку. Зняття вироблялося по наступному алгоритму. Кожен потік у нескінченному циклі перевіряв прапор-сигнал про завершення. Якщо прапор був виставлений, то потік виходив з нескінченного циклу і завершувався штатним шляхом. У спрощеному виді процедура зняття описана в лістингу 3.

Такий код ідеально працював, якщо вироблялося зняття потоку, що не звертається до графічної системи Windows (або рідко звертається - раз у кілька секунд). Якщо ж потік увесь час що-небудь малював, то спроба зняття закінчувалася виходом з функції WaitForSingleObject через перевищення часу чекання (значення, щоповертається, WAIT_TIMEOUT), тобто підпрограма, що знімається, не одержувала керування, поки ми "сиділи" у функції WaitForSingleObject. Збільшення періоду чекання (наприклад, до 10 с) ні до чого не приводило - потік усі десять секунд уперто чекав звільнення об'єкта і зрештою виходив зі значенням WAIT_TIMEOUT.

Причина, по якій потік не знімався, узагалі ж зрозуміла - йому не передавалося керування. Можна спробувати примусово зробити це, збільшивши пріоритет потоку, що знімається:

void breakTask(GF_Task* tsk)

{

DWORD result;

char s[512];

// команда потоку, що знімається, на зняття tsk->putState(tsBreak,True);

// збільшуємо відносний пріоритет

// потоку, що знімається, до максимально можливого

SetThreadPriority(tsk->TaskHnd95, THREAD_PRIORITY_TIME_CRITICAL)

// чекаємо завершення потоку протягом 1 з

WaitForSingleObject(tsk->TaskHnd95,1000);

}

Результату ніякого (вірніше, результат той же - вихід зі значенням WAIT_TIMEOUT). Виходить, що підвищення пріоритету не завжди спрацьовує (ще однією докір Microsoft).

Що ж робити? Як змусити потік, у якому працює програма зняття breakTask, передати керування іншим потокам? При одержанні значення WAIT_TIMEOUT починає виконуватися та частина коду, що виводить на екран вікно з запитом про те, що ж робити з потоком, що не знімається. У момент висновку вікна на екран багатостраждальний потік раптом сам завершується - він нарешті "зауважує" прапорець завершення і виходить з нескінченного циклу. Це підтверджує, що до потоку, що знімається, просто не доходить керування (не виділяється квант часу).

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

Отже, нам треба використовувати функцію типу GetMessage для стимуляції Windows до передачі керування іншим потокам. Але сама функція GetMessage нам не підходить, тому що вона віддає керування тільки в тому випадку, якщо для потоку з'явилося повідомлення. Замість GetMessage можна застосувати функцію PeekMessage, що перевіряє, є чи повідомлення в черзі для даного потоку, і незалежно від результату відразу ж повертає керування. Перепишемо наш попередній приклад так:

void breakTask(GF_Task* tsk)

{

DWORD result;

char s[512];

// команда потоку, що знімається, на зняття

tsk->putState(tsBreak,True);

// збільшуємо його відносний пріоритет

// до максимально можливого

SetThreadPriority(tsk->TaskHnd95,

THREAD_PRIORITY_TIME_CRITICAL)

int cnt = 1000/20;

// чекаємо завершення потоку протягом приблизно 1 з

while(cnt-)

{ // стимулюємо Windows до передачі кванта часу

// іншим потокам

PeekMessage(&_Msg,0,0,0,PM_NOREMOVE);

// чекаємо завершення потоку

result = WaitForSingleObject(tsk->TaskHnd95,20);

// якщо все-таки не дочекалися,

// те виходимо з циклу чекання

if(result!= WAIT_TIMEOUT)

break;

}

...і т.д.

}

У документації по SDK затверджується, що для передачі кванта часу іншим потокам можна викликати функцію Sleep з параметром 0 (Sleep(0)). Тому в літературі по системному програмуванню рекомендують для стимуляції Windows до передачі кванта часу синхронізувати потоки, використовуючи функцію PeekMessage.

Лістинг 1. Обмеження доступу до масиву з використанням критичних розділів

// Масив значень.

int mas[1000];

// Семафор, що регулює доступ до критичного розділу.

CRITICAL_SECTION CritSec;

{

...

// Инициализируем семафор критичного розділу.

InitializeCriticalSection(&CritSect);

... // Текст програми.

// Видаляємо об'єкт критичного розділу.

DeleteCriticalSection(&CritSec);

}

// Перший потік: запис у масив даних.

DWORD thread1(LPVOID par)

{ // Запис значення в масив.

// Запит на вхід у критичний розділ.

EnterCriticalSection(&CritSec);

// Виконання коду в критичному розділі.

for(int i = 0;i<1000;i++)

{

mas[i] = i;

}

// Вихід із критичного розділу:

// звільняємо критичний розділ для доступу

// до нього інших задач.

LeaveCriticalSection(&CritSec);

return 0;

}

// Другий потік: зчитування даних з масиву.

DWORD thread2(LPVOID par)

{ // Зчитування значень з масиву.

int j;

// Запит на вхід у критичний розділ.

EnterCriticalSection(&CritSec);

// Виконання коду в критичному розділі.

for(int i = 0;i<1000;i++)

{

j = mas[i];

}

// Вихід із критичного розділу:

// звільняємо критичний розділ для доступу

// до нього інших задач.

LeaveCriticalSection(&CritSec);

return 0;

}

Лістинг 2. Обмеження доступу до масиву з використанням семафорів, що виключають

// Масив значень.

int mas[1000];

// Об'єкт, що регулює доступ до поділюваного коду.

HANDLE CritMutex;

{

...

// Инициализируем семафор поділюваного коду.

CritMutex = SectCreateMutex(NULL,FALSE,NULL);

... // Текст програми.

// Закриваємо об'єкт доступу до поділюваного коду.

CloseHandle(CritMutex);

}

// Перший потік: запис у масив даних.

DWORD thread1(LPVOID par)

{ // Запис значень у масив.

// Запит на вхід у захищений розділ.

DWORD dw = WaitForSingleObject(CritMutex,INFINITE);

if(dw == WAIT_OBJECT_0)

{ // Якщо об'єкт звільнений коректно, те

// виконання коду в захищеному розділі.

for(int i = 0;i<1000;i++)

{

mas[i] = i;

}

// Вихід із захищеного розділу:

// звільняємо об'єкт для доступу

// до захищеного розділу інших задач.

ReleaseMutex(CritMutex);

}

return 0;

}

// Другий потік: зчитування даних з масиву.

DWORD thread2(LPVOID par)

{ // Зчитування значень з масиву.

int j;

// Запит на вхід у захищений розділ.

DWORD dw = WaitForSingleObject(CritMutex,INFINITE);

if(dw == WAIT_OBJECT_0)

{ // Якщо об'єкт звільнений коректно, те

// виконання коду в захищеному розділі.

for(int i = 0;i<1000;i++)

{

j = mas[i];

}

// Вихід із захищеного розділу:

// звільняємо об'єкт для доступу

// до захищеного розділу інших задач.

ReleaseMutex(CritMutex);

}

return 0;

}

Лістинг 3. Зняття графічного потоку

void breakTask(GF_Task* tsk)

{

DWORD result;

char s[512];

// Команда задачі, що знімається, на зняття.

tsk->putState(tsBreak,True);

// Чекаємо завершення потоку протягом 1 с.

WaitForSingleObject(tsk->TaskHnd95,1000);

//

// Аналіз відповіді.

//

if(result == WAIT_OBJECT_0) // Ok - потік довершений успішно.

{

result = cmOK;

goto _L_EndbreakTask;

}

else if(result == WAIT_TIMEOUT) // Потік не відповідає.

{ // Підготовляємо рядок запиту.

sprintf(s,,

"Потік # %і не відповідає...\nобїект %s\n Зробіть вибір: \n\

'Так' - повторити команду на зняття \n\

'Немає' - зняти потік примусово \n\

'Скасувати' - не знімати потік"

TaskCollection->indexOf(tsk)+1,

tsk->getName());

}

// Висновок запиту на екран.

result = MsgBox(s, msg|msgSound);

switch(result) // Аналіз відповіді.

{

case cmNo: // Примусове зняття потоку.

tsk->putState(tsCrash,True); // Виставляємо прапор

tsk->endTask(); // Заключні операції

TerminateThread(tsk->TaskHnd95,0); // Знімаємо потік

goto _L_EndbreakTask;

case cmCancel: // Скасування зняття потоку.

goto _L_EndbreakTask;

}

}

else if(WAIT_FAILED) // Відбулася помилка доступу до об'єкта.

{ // Примусове зняття потоку.

SIL(); // Звуковий сигнал

tsk->endTask(); // Заключні операції

TerminateThread(tsk->TaskHnd95,0);

SIL(); // Звуковий сигнал

result = cmNo;

goto _L_EndbreakTask;

}

}

_L_EndbreakTask:

CloseHandle(tsk->TaskHnd95);

tsk->TaskHnd95 = 0;

tsk->putState(tsWorkTask,False); // Знімаємо прапори

return result;

}

// Код потоку, що знімається, приблизно наступний:

DWORD thread1(LPVOID par)

{

while((state & tsBreak) == 0)

{ // Поки прапор tsBreak не виставлений, виконуємо потік.

draw() // Щось виводимо на екран.

}

return 0;

}

Завдання на виконання

Використовуючи компілятор С++, або Assembler реалізувати програму синхронізації згідно варіанту. Під час роботи програми треба весь час виводити інформацію про те як працюють потоки (у файл, або реалізувати графічну візуалізацію). Якщо у Вашому завданні мова іде про розподіл ресурсів між потоками треба фіксувати звільнення і зайняття ресурсів потоками. При використанні механізму подій та семафорів треба фіксувати переходи у вільні стани (події) та переходи у сигнальний стан (таймери).

Написати з використанням подій програму, що реалізують таку схему за допомогою подій. Нехай є клієнт та сервер, які повинні спілкуватись між собою. Спочатку сервер (це один потік) просто чекає. Клієнт (другий потік) приймає у користувача повідомлення та передає його серверу. Сервер обробляє повідомлення, а саме: він формує нове повідомлення яке складається з n повторів початкового повідомлення, де n - кількість символів у початковому повідомленні. Весь цей час клієнт чекає, потім отримує відповідь і виводить її користувачу.

Виконати завдання №1 використовуючи механізм таймерів. (треба виставити достатній час для обробки запитів.)

Написати програму, що має два потока; один готує дані (наприклад зчитує з файла), а інший відсилає їх на сервер. Треба розпаралелити їх роботу. Тут потоки повинні працювати по черзі. Спочатку перший потік готує порцію даних. Потім другий потік відправляє її, а перший тим часом готує наступну порцію і т.д. (для такої синхронізації потрібно буде дві події “автосбросом”).

Написати з використанням м'ютекса програму, що створює десять процесів, кожен з яких у свою чергу створює десять потоків, у кожному з потоків треба підрахувати значення факторіалу сумі свого номеру (a) та номеру свого процесу (b), потім занести номер свого процесу (1-10), свій номер (1-10), та обчислене значення у файл (вхід у стек). Після цього кожен потік повинен обчислити значення ступеню a^b. Знову, аналогічним чином відмітитись у файлі та, якщо можна, залишити відмітку про вихід зі стеку. Якщо вийти зі стеку неможливо чекати і потім вийти. Таким чином кожен потік пише у файл два рази або один раз. Переконатись у тому, що є потоки, які не чекають своєї черги вийти зі стеку. (Стек повинен спрацювати за стандартним правилом: перший зайшов - останній вийшов.)

Виконати завдання №4 використовуючи механізм подій (детально продумайте схему, прийдеться використовувати досить багато подій).

Виконати завдання №4 використовуючи критичні секції.

Виконати завдання №4 використовуючи семафор.

Виконати завдання №4 з тією різницею, що потоки повинні створити чергу. (Черга працює за правилом FIFO. Перший зайшов - перший вийшов. Поки не вийдуть всі потоки перед данним, він повинен чекати).

Виконати завдання №5 з тією різницею, що потоки повинні створити чергу. (Черга працює за правилом FIFO. Перший зайшов - перший вийшов. Поки не вийдуть всі потоки перед данним, він повинен чекати).

Виконати завдання №6 з тією різницею, що потоки повинні створити чергу. (Черга працює за правилом FIFO. Перший зайшов - перший вийшов. Поки не вийдуть всі потоки перед данним, він повинен чекати).

Виконати завдання №7 з тією різницею, що потоки повинні створити чергу. (Черга працює за правилом FIFO. Перший зайшов - перший вийшов. Поки не вийдуть всі потоки перед данним, він повинен чекати).

Написати програму (використовувати м'ютекс та семафор), що створює 5 клієнтських та 2 серверних потока. Для кожного потока зафіксувати деякий період часу, який він чекає, а потім поміщає у чергу свій номер та номер свого запиту (1,2 і т.д.). Серверні потоки нічого не роблять поки у черзі не з'явиться хоча б один єлемент. Як тільки він з'явився один з серверних потоків обробляє його (пише у файл свій номер та отриманий запит). Після обробки потік “засинає” на фіксований час. Якщо в черзі більше 10 запитів програма зупиняється.

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

Написати програму (використовуючи критичні секції та семафор), що аналогічна до програми з завдання №12. Кількість клієнтів - 2, кількість серверів - 4.

Використовуючи подію з ручним сбросом реалізувати наступне: Ваша програма повинна створити два потока. Один пише у файл (можна працювати просто з пам'ятю) 1000 одиниць і потім у циклі читає цей файл та змінює 1 на 0 та навпаки. (Після другої «прогонки» у файлі будуть лише нулю, потім тільки одиниці і т.д.). Другий потік повинен «просинатися» через фіксовані проміжки часу та підраховувати кількість одиниць у файлі.

За допомогою м'ютекса та таймера реалізувати наступне: Один потік просинається через однакові проміжки часу та додає 1 за модулем десять до числа у файлі (спочатку там нуль). Ваша програма повинна змоделювати будильник, що пікає кожну годину таке число разів, скільки записано у файлі.

Виконати завдання №15 використовуючи критичні секції та таймера.

Виконати завдання №15 використовуючи семафори та таймери.

Використовуючи подію і таймер написати програму, що створює два потока, що «працюють» по черзі (так само як у завданні №1, але потоком не треба обмінюватись повідомленнями). Ваша програма повинна моделювати будильник, який кожну годину дивиться який з процесів працює (№1 або №2) та пікає 1 або 2 рази відповідно.

За критичної секції та подій написати програму, що створює 11 потоків. Кожен з 10 перших потоків підраховує числа 10^10, 20^9, 30^8, 40^8, 50^7, 60^6, 70^5, 80^4, 90^2, 100^1 відповідно. Їх роботу необхідно синхронізувати таким чином, щоб вони по черзі писали результат у файл, а одинадцятий потік послідовно зчитував ці числа та додавав їх.

Написати програму з завдання №19 із використанням семафорів та подій.

Завдання на виконання

Використовуючи компілятор С++, або Assembler реалізувати програму синхронізації згідно варіанту:

1.Перевірити твердження SDK про те, що потоки мають дисципліну захоплення мютекса за правилом FIFO

2.Перевірити порядок виконання потоків після сигналізації події(event)

3. Написати бібліотеку роботи з комплексними числами в багатопоточному середовищі за допомогою критичної секції (одне блокування на всі потоки)

4.Написати бібліотеку роботи з комплексними числами в багатопоточному середовищі за допомогою повязаного з комплексним числом м'ютекса

5.Написати бібліотеку роботи з комплексними числами в багатопоточному середовищі за допомогою пов`язаного з комплексним числом мютекса(окремо читання і для запису)

6. Написати бібліотеку роботи з векторами в багатопоточному середовищі за допомогою критичної секції (одне блокування на всі потоки)

7.Написати бібліотеку роботи з векторами в багатопоточному середовищі за допомогою повязаного з вектором м'ютекса

8.Написати бібліотеку роботи з векторами в багатопоточному середовищі за допомогою пов'язаного з вектором м'ютекса(окремо читання і для запису)

9.З`ясувати дисципліну виконання потоків, які стояли в черзі по мірі звільнення семафора

10.Організувати синхронізацію між іменованими створеними обєктами за рахунок подій(event)

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

12.Написати бібліотеку роботи зі стеком в багатопоточному середовищі за допомогою повязаного з вектором м'ютекса

13.Написати бібліотеку роботи зі стеком в багатопоточному середовищі за допомогою критичної секції (одне блокування на всі потоки)

14.Написати бібліотеку роботи зі стеком в багатопоточному середовищі за допомогою пов'язаного з вектором м'ютекса(окремо читання і для запису)

Страницы: 1, 2, 3



© 2003-2013
Рефераты бесплатно, курсовые, рефераты биология, большая бибилиотека рефератов, дипломы, научные работы, рефераты право, рефераты, рефераты скачать, рефераты литература, курсовые работы, реферат, доклады, рефераты медицина, рефераты на тему, сочинения, реферат бесплатно, рефераты авиация, рефераты психология, рефераты математика, рефераты кулинария, рефераты логистика, рефераты анатомия, рефераты маркетинг, рефераты релиния, рефераты социология, рефераты менеджемент.