48005 (572117), страница 2

Файл №572117 48005 (Особливості багатозадачності в середовищі Windows) 2 страница48005 (572117) страница 22016-07-29СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 2)

2. Закриття (знищення) об'єкта mutex

BOOL CloseHandle(HANDLE hObject)

3. Універсальна функція запиту доступу

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) - універсальна функція, призначена для запиту доступу до синхронізуючого об'єкту (у даному випадку до об'єкта mutex).

hHandle - покажчик на синхронізуючий об'єкт (у даному випадку передається значення, повернуте функцією CreateMutex);

dwMilliseconds - час (у міллісекундах), протягом якого відбувається чекання звільнення об'єкта mutex. Якщо передати значення INFINITE (нескінченність), то функція буде чекати нескінченно довго.

Дана функція може повертати наступні значення:

WAIT_OBJECT_0 - об'єкт звільнився;

WAIT_TIMEOUT - час чекання звільнення пройшов, а об'єкт не звільнився;

WAIT_ABANDON - відбулося відмовлення від об'єкта (тобто процес, що володіє даним об'єктом, завершилося, не звільнивши об'єкт). У цьому випадку система (а не "процес-власник") переводить об'єкт у вільний стан. Таке звільнення об'єкта не припускає гарантій у захищеності даних;

WAIT_FAILED - відбулася помилка.

4. Звільнення об'єкта mutex

BOOL ReleaseMutex(HANDLE hMutex) - звільняє об'єкт mutex, переводячи його з зайнятого у вільний стан.

Чи дійде черга до вас?

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

Непрямим підтвердженням вищевикладених міркувань може служити той факт, що Microsoft не передбачила підтримку черговості запитів на доступ до об'єкта синхронізації. Тобто якщо кілька процесів очікують звільнення того самого об'єкта синхронізації, то немає ніякої можливості довідатися, який саме з них першим одержить доступ до об'єкта, що звільнився.

Пояснимо це на наступному прикладі. Нехай, трьом потокам необхідно звернутися до однієї ділянки коду, причому одноразово ця ділянка повинна виконувати тільки один потік. Введемо об'єкт синхронізації mutex, що регулює доступ потоків до цієї ділянки коду. Коли потік 1 захопив об'єкт mutex і став виконувати поділювану ділянку коду, потік 2 запросив дозвіл на доступ (тобто викликав функцію WaitForSingleObject), а система перевела потік 2 у режим чекання. Через якийсь час потік 3 теж запросив дозвіл на вхід у цей код і теж перейшов у режим чекання. Тепер, якщо потік 1 звільнить об'єкт синхронізації, те невідомо, який потік (2 чи 3) його захопить, - усі залежить від того, хто з них першим одержить свій квант часу для продовження роботи. Нехай об'єктом синхронізації заволодів потік 3, а поки він виконував поділюваний розділ, потік 1 знову запросив доступ до об'єкта синхронізації - і знову стало два конкуруючих потоки (1 і 2). І хто з них першим "достукається" до ділянки коду, що виконується, невідомо: може случитися так, що потік 2 ніколи не буде допущений до бажаної ділянки коду і надовго залишиться в стані чекання... А як відомо, гірше немає чекати, хоча потоку це байдуже. Інша справа - вам...

Події

Подія - це об‘єкт синхронізації, стан якого може бути установлений сигнальним шляхом виклику функцій SetEvent або PulseEvent. Існує два типа подій:

Тип об‘єкту

Опис

Подія з ручним “сбросом”

Це об‘єкт, сигнальний стан якого зберігається до ручного” сброса” функцією ResetEvent. Як тільки стан об‘єкту установлений в сигнальний, всі потоки, що знаходяться в циклі очікування цього об‘єкту, продовжують своє виконання (звільняються).

Подія з автоматичним “сбросом”

Об‘єкт, сигнальний стан якого зберігається до тих пір, поки не буде звільнений єдиний потік, після чого система автоматично установлює несигнальний стан події. Якщо нема потоків, очікуючих цієї події, об‘єкт залишається в сигнальному стані.

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

Потік може використовувати функцію CreateEvent для створення об‘єкту подія. Створюючий подію потік встановлює її початковий стан. В цьому потоці можна вказати ім‘я події. Потоки інших процесів можуть отримати доступ до цієї події по імені, вказав його у функції OpenEvent.

Потік може використовувати функцію PulseEvent для установки стану події - сигнальним і потім “сбросить” стан в несигнальне значення після звільнення відповідної кількості очікуючих потоків. У випадку об‘єктів з ручним “сбросом” звільняються всі очікуючі потоки. У випадку об‘єктів з автоматичним “сбросом” звільняється тільки єдиний потік, навіть якщо цієї події очікують декілька потоків. Якщо очікуючих потоків нема, PulseEvent просто встановлює стан подій - несигнальний.

#include

#include

#include

HANDLE hEvent1, hEvent2;

int a[ 5 ];

void Thread(void* pParams)

{

int i, num = 0;

while (TRUE)

{

WaitForSingleObject(hEvent2, INFINITE);

for (i = 0; i < 5; i++) a[ i ] = num;

SetEvent(hEvent1);

num++;

}

}

int main(void)

{

hEvent1 = CreateEvent(NULL, FALSE, TRUE, NULL);

hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);

_beginthread(Thread, 0, NULL);

while(TRUE)

{

WaitForSingleObject(hEvent1, INFINITE);

printf("%d %d %d %d %d\n",

a[ 0 ], a[ 1 ], a[ 2 ],

a[ 3 ], a[ 4 ]);

SetEvent(hEvent2);

}

return 0;

}

Таймери, що чекають

Мабуть, таймери, що очікують - самий витончений об‘єкт ядра для синхронізації. З‘явились вони, починаючи з Windows 98. Таймери створюються функцією CreateWaitableTimer і бувають, також як і події, з автосбросом і без нього. Потім таймер треба настроїти функцією SetWaitableTimer. Таймер переходить в сигнальний стан, коли закінчується його таймаут. Відмінити "цокання" таймера можна функцією CancelWaitableTimer. Відмітимо, що можна указати callback функцію при установці таймера. Вона буде виконуватись, коли спрацьовує таймер.

Приклад. Напишемо програму-будильник використовуючи WaitableTimer'и. Будильник буде спрацьовувати раз в день в 8 ранку и "пікати" 10 раз. Використовуємо для цього два таймера, один з яких с callback-функцією.

#include

#include

#include

#include

#define HOUR (8) // the time of alarm

#define RINGS (10) // number of rings

HANDLE hTerminateEvent;

// callback – timer function

VOID CALLBACK TimerAPCProc(LPVOID, DWORD, DWORD)

{

Beep(1000,500); // ringing!

};

// thread function

unsigned __stdcall ThreadFunc(void *)

{

HANDLE hDayTimer = CreateWaitableTimer(NULL,FALSE,NULL);

HANDLE hAlarmTimer = CreateWaitableTimer(NULL,FALSE,NULL);

HANDLE h[2]; // we will wait for these objects

h[0] = hTerminateEvent; h[1] = hDayTimer;

int iRingCount=0; // number "rings"

int iFlag;

DWORD dw;

// we should convert the time into FILETIME format

// timer don’t understand other formats

LARGE_INTEGER liDueTime, liAllDay;

liDueTime.QuadPart=0;

// day in 100-nanosecond intervals = 10000000 * 60 * 60 * 24 = 0xC92A69C000

liAllDay.QuadPart = 0xC9;

liAllDay.QuadPart=liAllDay.QuadPart << 32;

liAllDay.QuadPart |= 0x2A69C000;

SYSTEMTIME st;

GetLocalTime(&st); // current day and time

iFlag = st.wHour > HOUR; // if the Time don’t come

// than we set alarm for today, if not than for tomorrow

st.wHour = HOUR;

st.wMinute = 0;

st.wSecond =0;

FILETIME ft;

SystemTimeToFileTime(&st, &ft);

if (iFlag)

((LARGE_INTEGER *)&ft)->QuadPart =

((LARGE_INTEGER *)&ft)->QuadPart +liAllDay.QuadPart;

LocalFileTimeToFileTime(&ft,&ft);

// Installing the timer,

// it will alarm once a day

SetWaitableTimer(hDayTimer, (LARGE_INTEGER *) &ft, 24*60*60000, 0, 0, 0);

do {

dw = WaitForMultipleObjectsEx(2,h,FALSE,INFINITE,TRUE);

if (dw == WAIT_OBJECT_0 +1) // hDayTimer

{

SetWaitableTimer(hAlarmTimer, &liDueTime, 1000, TimerAPCProc, NULL, 0);

iRingCount=0;

}

if (dw == WAIT_IO_COMPLETION) // the callback-functionhas finished working

{

iRingCount++;

if (iRingCount==RINGS)

CancelWaitableTimer(hAlarmTimer);

}

}while (dw!= WAIT_OBJECT_0); // while hTerminateEvent is of

CancelWaitableTimer(hDayTimer);

CancelWaitableTimer(hAlarmTimer);

CloseHandle(hDayTimer);

CloseHandle(hAlarmTimer);

_endthreadex(0);

return 0;

};

int main(int argc, char* argv[])

{

// this event shows the thread when to finish working

hTerminateEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

unsigned uThreadID;

HANDLE hThread;

// creating thread

hThread = (HANDLE)_beginthreadex(NULL, 0, &ThreadFunc, 0, 0,&uThreadID);

puts("Press any key to exit.");

getch();

// setting the event

SetEvent(hTerminateEvent);

//waiting for closing of the thread

WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);

return 0;}

Багатопотоковість і графіка

Є ще одна особливість при роботі з об'єктами синхронізації. Справа в тім, що Windows 95 досить "важко" взаємодіє зі своєю графічною системою в багатозадачному режимі. Це пояснюється тим, що в Windows 95 графічна підсистема частково залишилася 16-розрядної і звертання до такого коду приводить до захоплення системного семафора, що виключає, Win16Mutex, що запобігає одночасний доступ декількох процесів (потоків) до такого коду. Твердження авторів деяких книг по Windows 95 про те, що це не є перешкодою, якщо ви працюєте в цілком 32-розрядному додатку, на практиці виявляється неспроможним.

Отже, основною проблемою стала неможливість коректного зняття з виконання графічного потоку. Зняття вироблялося по наступному алгоритму. Кожен потік у нескінченному циклі перевіряв прапор-сигнал про завершення. Якщо прапор був виставлений, то потік виходив з нескінченного циклу і завершувався штатним шляхом. У спрощеному виді процедура зняття описана в лістингу 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)

{

Характеристики

Тип файла
Документ
Размер
472,14 Kb
Учебное заведение
Неизвестно

Список файлов ответов (шпаргалок)

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
7027
Авторов
на СтудИзбе
260
Средний доход
с одного платного файла
Обучение Подробнее