Особливості багатозадачності в середовищі 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)
{ // Якщо обєкт звільнений коректно, те
// виконання коду в захищеному р?/p>