Особливості багатозадачності в середовищі Windows
Контрольная работа - Компьютеры, программирование
Другие контрольные работы по предмету Компьютеры, программирование
токів, очікуючих цієї події, обєкт залишається в сигнальному стані.Події корисні в тих випадках, коли необхідно послати повідомлення потоку, яке сповіщає, що відбулася певна подія. Наприклад, при асинхронних операціях вводу и виводу з одного пристрою, система установлює подію в сигнальний стан коли закінчується якась із цих операцій. Один потік може використовувати декілька різних подій в декількох операціях, що перекриваються, а потім очікувати приходу сигналу від любого з них.
Потік може використовувати функцію 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 dont 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
HOUR;//iftheTimedon">iFlag = st.wHour > HOUR; // if the Time dont 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 =
QuadPart+liAllDay.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 (або рідко звертається - раз у кілька секунд). Якщо ж потік увесь ч