Читайте также: |
|
Struct Query { // описание структуры запроса
LONG lClientId;
char cQueryText[256]; };
struct Query Queue[20]; // создание очереди
LONG lPreviousCount=0;
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{ int i; DWORD dwResult=0;
struct Query Query1; // обрабатываемый запрос
struct PARAM *p= (struct PARAM *) pvParam;
while (!p->stop) {
// ожидание наличия в очереди запросов
WaitForSingleObject(hSem, INFINITE); // извлечение 1-го запроса из очередиEnterCriticalSection(&cs);
Query1 = Queue[0];
for (i=0; i< lPreviousCount; i++)
Queue [i]= Queue [i+1];
LeaveCriticalSection(&cs);
// обработка запроса Query1 в потоке
…
}
return(dwResult);
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{ // содержимое функции потока 2
// аналогично потоку 1
…
}
// Функция потока 3
DWORD WINAPI ThreadFunc3(PVOID pvParam)
{ // содержимое функции потока 3
// аналогично потоку 1
…
}
// --- Функция WinMain
int APIENTRY WinMain(…)
{
struct Query InQuery; // буфер для нового запроса
// Создание объекта семафор
hSem = CreateSemaphore(NULL, 0, 20, NULL);// Создание объекта критический раздел
InitializeCriticalSection(&cs);…
// получение запроса от клиента и помещение
// его в очередь
InQuery = GetQuery();
// увеличение числа запросов и счетчика на 1
if (ReleaseSemaphore(hSem, 1, &lPreviousCount))
{
EnterCriticalSection(&cs);
Queue[lPreviousCount] = InQuery;
LeaveCriticalSection(&cs);
}
else // очередь заполнена, запросу отказано
RejectQuery(InQuery);
…
// Удаление объектов семафор и
// критической раздел (при необходимости)
CloseHandle(hSem);
DeleteCriticalSection(&cs);
…
}
События
1.
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttrib,
BOOL fManualReset, BOOL flnitialState, LPCTSTR lpName);
2.
BOOL SetEvent(HANDLE hEvent);
3.
BOOL ResetEvent(HANDLE hEvent);
4.
BOOL PulseEvent(HANDLE hEvent);
5.
BOOL CloseHandle(HANDLE hEvent);
Примеры использования событий.
Пример 1. Два потока по очереди выполняют операции записи-чтения в блоке памяти. Используется одно событие с автосбросом.
HANDLE hEvent;
hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освождения события hEvent
WaitForSingleObject(hEvent, INFINITE); // выполнение операций записи-чтения потоком 1…
SetEvent(hEvent);
}
…
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освождения события hEvent
WaitForSingleObject(hEvent, INFINITE); // выполнение операций записи-чтения потоком 2…
SetEvent(hEvent);
}
…
}
Пример 2. Два потока параллельно выполняют операции чтения из блока памяти. Используется одно событие с ручным сбросом.
HANDLE hEvent;
hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освождения события hEvent
WaitForSingleObject(hEvent, INFINITE); // выполнение операций чтения потоком 1}
…
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освождения события hEvent
WaitForSingleObject(hEvent, INFINITE); // выполнение операций чтения потоком 2}
…
}
Пример 3. Два потока последовательно выполняют операции ввода данных и их обработки. Используются два события с автосбросом, одно в свободном состоянии, другое – в занятом.
HANDLE hEvent1, hEvent2;
hEvent1=CreateEvent(NULL, FALSE, TRUE, NULL);
hEvent2=CreateEvent(NULL, FALSE, FALSE, NULL);
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освождения события hEvent1
WaitForSingleObject(hEvent1, INFINITE); // выполнение операций ввода данных потоком 1…
// перевод события hEvent2 в свобод. состояние
SetEvent(hEvent2);
}
…
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освождения события hEvent2
WaitForSingleObject(hEvent2, INFINITE); // выполнение обработки данных потоком 2…
// перевод события hEvent1 в свобод. состояние
SetEvent(hEvent1);
}
…
}
Ожидаемые таймеры
Ожидаемые таймеры (waitable timers) — это объекты ядра, которые самостоятельно переходят в свободное состояние в определенное время или через регулярные промежутки времени.
Ожидаемые таймеры бывают двух типов: со сбросом вручную и с автосбросом. Когда освобождается таймер со сбросом вручную, возобновляется выполнение всех потоков, ожидавших этот объект, а когда в свободное состояние переходит таймер с автосбросом — лишь одного из потоков.
Функции для работы с ожидаемыми таймерами следующие:
1. Создание объекта "ожидаемый таймер" производится следующей функцией:
HANDLE CreateWaitableTimer(LPSECURITY_ATTRIBUTES lpWTimerAttrib, BOOL fManualReset, LPCTSTR lpName);
Параметры функции:
- lpWTimerAttrib является указателем на структуру типа SECURITY_ATTRIBUTES, если не используется, то задается NULL;
- fManualReset указывает, какого типа таймер будет создан: со сбросом вручную (TRUE) или с автосбросом (FALSE);
- lpName указывает на строку, содержащую имя таймера, которое необходимо для доступа к объекту других процессов. Если не нужен именованный объект, то указывается NULL.
Функция CreateWaitableTimerвозвращает описатель таймера, используемый для работы с ним.
Объекты «ожидаемый таймер» всегда создаются в занятом состоянии.
2. Задание времени и периодичности, когда ожидаемый таймер будет переходить в свободное состояние производится следующей функцией:
BOOL SetWaitableTimer(HANDLE hWTimer,
const LARGE_INTEGER *pDueTime, LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
PVOID pvArgToCompletionRoutine, BOOL fResume);
где
- hWTimer – дескриптор ожидаемого таймера, возвращенный функцией CreateWaitableTimer;
- pDueTime указывает на целое 64-битное значение, где хранится дата и время (в формате UTC - Coordinated Universal Time) первого перехода таймера в свободное состояние. Если это значение имеет отрицательный знак, то его абсолютная величина указывает, через какой промежуток (в интервалах по 100 нс) после вызова данной функции таймер станет свободным;
- lPeriod задает периодичность (в миллисекундах) следующих переходов таймера в свободное состояние;
- pfnCompletionRoutine и pvArgToCompletionRoutine задают имя APC-функции, вызываемой при переходе таймера в свободное состояние и адрес передаваемых ей данных. APC – это асинхронный вызов функций (Asynchronous Procedure Call). Если такая функция не используется, то в обеих параметрах указываются NULL;
- fResume используется на компьютерах, поддерживающих режим сна. Если он равен TRUE, то при срабатывании таймера компьютер выйдет из режима сна и "разбудит" потоки, ожидающие этот таймер. В противном случае объект-таймер перейдет в свободное состояние, но ожидавшие его потоки не получат процессорное время, пока компьютер не выйдет из режима сна.
Функция возвращает TRUE, если ее вызов выполнен успешно и FALSE в противном случае.
3. Отключение срабатываний таймера производится функцией:
BOOL CancelWaitableTimer(HANDLE hWTimer);
После этого восстановить работу таймера можно повторным вызовом функции SetWaitableTimer() с заданием новых времени и периодичности.
4. Уничтожение объекта hWTimer и освобождение его дескриптора производится функцией:
BOOL CloseHandle(HANDLE hWTimer);
Взаимодействие потоков с ожидаемыми таймерами происходит следующим образом.
После создания ожидаемого таймера, он должен быть настроен на первое срабатывание функцией SetWaitableTimer(). При наступлении заданного момента времени ожидаемый таймер переходит в свободное состояние и в зависимости от его типа происходит следующее:
1) если таймер с автосбросом, то он автоматически сбрасывается в занятое состояние, при этом только один поток будет работать, а остальные потоки останутся ждать. Для возобновления работы потоков, ожидающих данного таймера необходимо вызвать функцию SetWaitableTimer(), задав новое время его срабатывания;
2) если таймер со сбросом вручную, то все потоки, ожидающие этот таймер, получат управление, а он так и останется в свободном состоянии, пока какой-нибудь поток не вызовет CancelWaitableTimer(). После этого возобновить работу таймера можно вызовом функции SetWaitableTimer() с заданием новых времени и периодичности.
Ожидаемые таймеры отличаются от обычных таймеров Windows (работающих в пользовательском режиме и настраиваемых функцией SetTimer()) следующим: во-первых они являются объектами ядра, а значит более защищенными; во-вторых они более точные, т.к. сообщения WM_TIMER, посылаемые обычными таймерами имеют низкий приоритет и в очереди сообщений обрабатываются последними.
Примечание: рассмотренные выше функции Win32 API для работы с ожидаемыми таймерами появились позже других функций для синхронизации потоков, начиная с версий Windows NT 4.0 и Windows 98. Поэтому описания прототипов этой группы функций в заголовочном файле WINBASE.H заключены в блок условной компиляции:
#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
… // Прототипы функций для работы с ожид. таймерами
#endif
Тем самым ограничивается возможность использования этих функций в ранних версиях ОС Windows.
При работе с ожидаемыми таймерами в программе необходимо перед включением заголовочного файла StdAfx.h определить один из указанных в WINBASE.H идентификаторов:
#define _WIN32_WINNT 0x0400
#include <stdafx.h>
Примеры использования ожидаемых таймеров. (См. электронный вар-т)
Пример 1. Два потока синхронизируются объектом "ожидаемый таймер" с автосбросом. Время срабатывания таймера является относительным и задается в количестве интервалов по 100 нс. Первое срабатывание – через 10 мс после создания таймера, последующие – через 2 мс после завершения потоком своих операций. (1 мс равна 10000 интервалов по 100 нс).
#define _WIN32_WINNT 0x0400
…
HANDLE hWTimer;
LARGE_INTEGER li;
hWTimer=CreateWaitableTimer(NULL, FALSE, NULL);
// Задаем отрицательное число в количестве
// интервалов по 100 нс
li.QuadPart = - (10 * 10000);
// Устанавливаем первое срабатывание через 10 мс
SetWaitableTimer(hWTimer,&li, 0, NULL, NULL, FALSE);
…
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освобождения таймера hWTimer
WaitForSingleObject(hWTimer, INFINITE); // выполнение операций потоком 1 ...// Задаем следующее срабатывание через 2 мс
li.QuadPart = - (2 * 10000);
SetWaitableTimer(hWTimer,&li,0,NULL,NULL,FALSE);
}
…
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освобождения таймера hWTimer
WaitForSingleObject(hWTimer, INFINITE); // выполнение операций потоком 2 ...// Задаем следующее срабатывание через 2 мс
li.QuadPart = - (2 * 10000);
SetWaitableTimer(hWTimer,&li,0,NULL,NULL,FALSE);
}
…
}
Пример 2. Устанавливаем ожидаемый таймер, чтобы он срабатывал ежедневно в 12.00 (время перерыва на обед) и вызывал APC-функцию, которая выдает звуковой сигнал 1000 гц.
#define _WIN32_WINNT 0x0400
…
HANDLE hWTimer;
SYSTEMTIME st;
FILETIME ftLocal, ftUTC;
LARGE_INTEGER liUTC;
// callback функция таймераVOID CALLBACK TimerAPCProc(LPVOID, DWORD, DWORD){ Beep(1000,500); // выдается звуковой сигнал};...
// создаем таймер с автосбросом
hWTimer = CreateWaitableTimer(NULL, FALSE, NULL);
// узнаем текущую дату/время GetLocalTime(&st); // если назначенный час уже наступил, // то ставим время на завтраif (st.wHour > 12) st.wDay++; st.wHour = 12;st.wMinute = 0;st.wSecond = 0;// преобразуем время из SYSTEMTIME в FILETIMESystemTimeToFileTime(&st, &ftLocal);
// преобразуем местное время в UTC-время
LocalFileTimeToFileTime(&ftLocal, &ftUTC);
// преобразуем FILETIME в LARGE_INTEGER из-за
// различий в выравнивании данных
liUTC.LowPart = ftUTC.dwLowDateTime;
liUTC.HighPart = ftUTC.dwHighDateTime;
// устанавливаем таймер
SetWaitableTimer(hWTimer, &liUTC, 24*60*60*1000, TimerAPCProc, NULL, FALSE);
...
Дата добавления: 2015-07-10; просмотров: 217 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
В том-то и признак настоящего искусства, что оно всегда современно, насущно, полезно» (Ф.М. Достоевский) (по произведениям 20 века). | | | От автора |