Издание четвертое windows ® для профессионалов создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows



Pdf просмотр
страница30/68
Дата28.11.2016
Размер3.57 Mb.
Просмотров12468
Скачиваний0
1   ...   26   27   28   29   30   31   32   33   ...   68
Г ЛАВА 10
Полезные средства для синхронизации потоков
Плоды своих трудов я инкапсулировал в C++ класс CSWMRG (я произношу его название как
swimerge); это аббревиатура от «single writer/multiple reader guard». Он содержится в файлах SWMRG.h и SWMRG.cpp (см. листинг на рис. 10 Использовать CSWMRG проще простого. Вы создаете объект C++ класса и вызываете нужные в Вашей программе функции члены. В этом классе всего три метода (не считая конструктора и деструктора):
VOID CSWMRG::WaitToRead();
// доступ к разделяемому ресурсу для чтения CSWMRG::WaitToWrite();
// монопольный доступ к разделяемому ресурсу для записи CSWMRG::Done();
// вызывается по окончании работы с ресурсом
Первый метод (
WaitToRead) вызывается перед выполнением кода, что либо считывающего из разделяемого ресурса, а второй (
WaitToWrite) — перед выполнением кода,
который считывает и записывает данные в разделяемом ресурсе. К последнему методу) программа обращается, закончив работу с этим ресурсом. Куда уж проще, а?
Объект CSWMRG содержит набор переменных членов (см. таблицу ниже, отражающих то, как потоки работают с разделяемым ресурсом на данный момент. Остальные подробности Вы узнаете из исходного кода.
Переменная
Описание
m_cs
Охраняет доступ к остальным членам класса, обеспечивая операции сними на атомарном уровне.
m_nActive
Отражает текущее состояние разделяемого ресурса. Если она равна ни один поток к ресурсу не обращается. Ее значение, большее 0, сообщает текущее число потоков, считывающих данные из ресурса.
Отрицательное значение (–1) свидетельствует о том, что какой то поток записывает данные в ресурс.
m_nWaitingReaders
Сообщает количество потоков читателей, которым нужен доступ к ресурсу. Значение этой переменной инициализируется 0 и увеличивается на 1 всякий раз, когда поток вызывает
WaitToRead в то время, как
m_nActive равна –1.
m_nWaitingWriters
Сообщает количество потоков писателей, которым нужен доступ к ресурсу. Значение этой переменной инициализируется 0 и увеличивается на 1 всякий раз, когда поток вызывает
WaitToWrite в то время, как
m_nActive больше 0.
m_hsemWriters
Когда потоки писатели вызывают
WaitToWrite, но получают отказ в доступе, так как
m_nActive больше 0, они переходят в состояние ожидания этого семафора. Пока ждет хотя бы один поток писатель, новые потоки читатели получают отказ в доступе к ресурсу.
Тем самым я не даю потокам читателям монополизировать доступ к этому ресурсу. Когда последний поток читатель, работавший с ресурсом, вызывает
Done, семафор освобождается со счетчиком,
равным 1, и система пробуждает один ждущий поток «писатель».
m_hsemReaders
Когда потоки читатели вызывают
WaitToRead, но получают отказ в доступе, так как
m_nActive равна –1, они переходят в состояние ожидания этого семафора. Когда последний из ждущих потоков
«писателей» вызывает
Done, семафор освобождается со счетчиком,
равным
m_nWaitingReaders, и система пробуждает все ждущие потоки «читатели».
Программа-пример SWMRG
Эта программа, «10 SWMRG.exe» (см. листинг на рис. 10 3), предназначена для тестирования+ класса CSWMRG. Файлы исходного кода и ресурсов этой программы

270
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
находятся в каталоге 10 SWMRG на компакт диске, прилагаемом к книге. Я запускаю это приложение под управлением отладчика, чтобы наблюдать за всеми функциями и переменными — членами классов.
При запуске программы первичный поток создает несколько потоков, выполняющих одну и туже функцию. Далее первичный поток вызывает
WaitForMultipleObjects и ждет завершения этих потоков. Когда все они завершаются, их описатели закрываются и процесс прекращает свое существование.
Каждый вторичный поток выводит на экран такое сообщение:
Чтобы данный поток имитировал чтение ресурса, щелкните кнопку Yes, а чтобы он имитировал запись в ресурс — кнопку No. Эти действия просто заставляют его вызвать либо функцию
WaitToRead, либо функцию WaitToWrite объекта После вызова одной из этих функций поток выводит соответствующее сообщение.
Пока окно с сообщением открыто, программа приостанавливает потоки делает вид, будто он сейчас работает с ресурсом.
Конечно, если какой то поток читает данные из ресурса и Вы командуете другому потоку записать данные в ресурс, окно с сообщением от последнего на экране не появится, так как поток писатель ждет освобождения ресурса, вызвав
WaitToWrite.
Аналогичным образом, если Вы скомандуете потоку считать данные из ресурса в то время, как показывается окно с сообщением от потока писателя, первый поток будет ждать в вызове
WaitToRead, и его окно не появится до тех пор, пока все потоки
«писатели» не закончат имитировать свою работу с ресурсом.
Закрыв окно с сообщением (щелчком кнопки OK), Вы заставите поток, получивший доступ к ресурсу, вызвать
Done, и объект CSWMRG переключится на другие ждущие потоки.
SWMRG.cpp
/******************************************************************************
Модуль: Автор Copyright (c) 2000, Джеффри Рихтер (Jeffrey Richter)
******************************************************************************/
#include "..\CmnHdr.h"
/* см. приложение А */
#include "SWMRG.h"
///////////////////////////////////////////////////////////////////////////////
Рис. 10-3.
Программа-пример SWMRG

271
Г ЛАВА 10
Полезные средства для синхронизации потоков
Рис. 10-3.
продолжение
CSWMRG::CSWMRG() {
// изначально ресурс никому ненужен, и никто к нему не обращается m_nWaitingReaders = m_nWaitingWriters = m_nActive = 0;
m_hsemReaders = CreateSemaphore(NULL, 0, MAXLONG, NULL);
m_hsemWriters = CreateSemaphore(NULL, 0, MAXLONG, NULL);
InitializeCriticalSection(&m_cs);
}
///////////////////////////////////////////////////////////////////////////////
CSWMRG::
CSWMRG() {
#ifdef _DEBUG
// SWMRG нельзя уничтожать, если потоки пользуются ресурсом if (m_nActive != 0)
DebugBreak();
#endif m_nWaitingReaders = m_nWaitingWriters = m_nActive = 0;
DeleteCriticalSection(&m_cs);
CloseHandle(m_hsemReaders);
CloseHandle(m_hsemWriters);
}
///////////////////////////////////////////////////////////////////////////////
VOID CSWMRG::WaitToRead() {
// обеспечиваем монопольный доступ к переменным членам работает ли сейчас с ресурсом какой нибудь поток "писатель и есть ли "писатели, ждущие этот ресурс fResourceWritePending = (m_nWaitingWriters || (m_nActive < 0));
if (fResourceWritePending) {
// этот "читатель" должен ждать,
// увеличиваем счетчик числа ждущих "читателей" на 1
m_nWaitingReaders++;
} else {
// этот "читатель" может читать,
// увеличиваем счетчик числа активных "читателей" на 1
m_nActive++;
}
// разрешаем другим потокам попытаться получить доступ для чтения или записи
LeaveCriticalSection(&m_cs);
if (fResourceWritePending) {
см. след. стр.

272
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
Рис. 10-3.
продолжение
// этот поток должен ждать, INFINITE);
}
}
///////////////////////////////////////////////////////////////////////////////
VOID CSWMRG::WaitToWrite() {
// обеспечиваем монопольный доступ к переменным членам работают ли сейчас с ресурсом какие нибудь потоки fResourceOwned = (m_nActive != 0);
if (fResourceOwned) {
// этот "писатель" должен ждать увеличиваем счетчик числа ждущих "писателей" на 1
m_nWaitingWriters++;
} else {
// этот "писатель" может писать уменьшаем счетчик числа активных "писателей" до 1
m_nActive =
1;
}
// разрешаем другим потокам попытаться получить доступ для чтения или записи (fResourceOwned) {
// этот поток должен ждать, INFINITE);
}
}
///////////////////////////////////////////////////////////////////////////////
VOID CSWMRG::Done() {
// обеспечиваем монопольный доступ к переменным членам (m_nActive > 0) {
// ресурс контролируют "читатели, значит, убираем одного из них так m_nActive
;
} else {
// ресурс контролируют "писатели, значит, убираем одного из них так

273
Г ЛАВА 10
Полезные средства для синхронизации потоков
Рис. 10-3.
продолжение
m_nActive++;
}
HANDLE hsem = NULL; // предполагаем, что ждущих потоков нет lCount = 1;
// предполагаем, что пробуждается только один ждущий поток (в отношении "писателей" это всегда так (m_nActive == 0) {
// Ресурс свободен, кого пробудить Примечание "читатели" никогда не получат доступ к ресурсу если его всегда будут ждать "писатели (m_nWaitingWriters > 0) {
// ресурс ждут "писатели, а они имеют приоритет перед "читателями =
1;
// писатель получит доступ m_nWaitingWriters
;
// одним ждущим "писателем" станет меньше hsem = m_hsemWriters;
// "писатели" ждут на этом семафоре Примечание семафор откроет путь только одному потоку "писателю else if (m_nWaitingReaders > 0) {
// ресурс ждут "читатели, а "писателей" нет m_nActive = m_nWaitingReaders;
// все "читатели" получат доступ m_nWaitingReaders = 0;
// ждущих "читателей" не останется hsem = m_hsemReaders;
// "читатели" ждут на этом семафоре lCount = m_nActive;
// семафор откроет путь всем "читателям else {
// ждущих потоков вообще нет разрешаем другим потокам попытаться получить доступ для чтения или записи (hsem != NULL) {
// некоторые потоки следует пробудить, lCount, NULL);
}
}
//////////////////////////////// Конец файла //////////////////////////////////
SWMRG.h
/******************************************************************************
Модуль: Автор Copyright (c) 2000, Джеффри Рихтер (Jeffrey Richter)
******************************************************************************/
см. след. стр.

274
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
Рис. 10-3.
продолжение
#pragma once
///////////////////////////////////////////////////////////////////////////////
class CSWMRG {
public:
CSWMRG();
// конструктор деструктор
VOID WaitToRead();
// предоставляет доступ к разделяемому ресурсу для чтения WaitToWrite();
// предоставляет монопольный доступ к разделяемому ресурсу для записи Done();
// вызывается по окончании работы с ресурсом private:
CRITICAL_SECTION m_cs; // обеспечивает монопольный доступ к другим элементам m_hsemReaders;
// "читатели" ждут на этом семафоре если ресурс занят "писателем m_hsemWriters;
// "писатели" ждут на этом семафоре если ресурс занят "читателем m_nWaitingReaders; // число ждущих "читателей m_nWaitingWriters; // число ждущих "писателей m_nActive;
// текущее число потоков, работающих с ресурсом (0 — таких потоков нет, >0 — число "читателей один "писатель Конец файла //////////////////////////////////
SWMRGTest.cpp
/******************************************************************************
Модуль: Автор Copyright (c) 2000, Джеффри Рихтер (Jeffrey Richter)
******************************************************************************/
#include "..\CmnHdr.h"
/* см. приложение А */
#include
#include
// для доступа к _beginthreadex
#include "SWMRG.h"
///////////////////////////////////////////////////////////////////////////////
// глобальный синхронизирующий объект Single Writer/Multiple Reader Guard
CSWMRG g_swmrg;
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI Thread(PVOID pvParam) {
TCHAR sz[50];

275
Г ЛАВА 10
Полезные средства для синхронизации потоков
Рис. 10-3.
продолжение
wsprintf(sz, TEXT("SWMRG Test: Thread %d"), PtrToShort(pvParam));
int n = MessageBox(NULL,
TEXT("YES: Attempt to read\nNO: Attempt to write"), sz, MB_YESNO);
// попытка чтения или записи if (n == IDYES)
g_swmrg.WaitToRead();
else g_swmrg.WaitToWrite();
MessageBox(NULL,
(n == IDYES) ? TEXT("OK stops READING") : TEXT("OK stops WRITING"),
sz, MB_OK);
// прекращаем чтение или запись g_swmrg.Done();
return(0);
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) {
// порождаем серию потоков, пытающихся читать или записывать hThreads[MAXIMUM_WAIT_OBJECTS];
for (int nThreads = 0; nThreads < 8; nThreads++) {
DWORD dwThreadId;
hThreads[nThreads] =
chBEGINTHREADEX(NULL, 0, Thread, (PVOID) (DWORD_PTR) nThreads,
0, &dwThreadId);
}
// ждем завершения всех потоков
WaitForMultipleObjects(nThreads, hThreads, TRUE, INFINITE);
while (nThreads
)
CloseHandle(hThreads[nThreads]);
return(0);
}
//////////////////////////////// Конец файла //////////////////////////////////
Реализация функции WaitForMultipleExpressions
Некоторое время назад я разрабатывал одно приложение и столкнулся с весьма непростым случаем синхронизации потоков. Функции
WaitForMultipleObjects, заставляющей поток ждать освобождения одного или всех объектов, оказалось недостаточно.
Мне понадобилась функция, которая позволяла бы задавать более сложные критерии ожидания. У меня было три объекта ядра процесс, семафор и событие. Мой поток должен был ждать до тех пор, пока не освободится либо процесс и семафор, либо процесс и событие.

276
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
Слегка поразмыслив и творчески использовав имеющиеся функции Windows, я создал именно то, что мне требовалось, — функцию
WaitForMultipleExpressions. Ее прототип выглядит так WINAPI WaitForMultipleExpressions(
DWORD nExpObjects,
CONST HANDLE* phExpObjects,
DWORD Перед ее вызовом Вы должны создать массив описателей (HANDLE) и инициализировать все его элементы. Параметр
nExpObjects сообщает число элементов в массиве, на который указывает параметр
phExpObjects. Этот массив содержит несколько наборов описателей объектов ядра при этом каждый набор отделяется элементом,
равным NULL. Функция
WaitForMultipleExpressions считает все объекты водном наборе объединяемыми логической операцией AND, асами наборы — объединяемыми логической операцией OR. Поэтому
WaitForMultipleExpressions приостанавливает вызывающий поток до тех пор, пока не освободятся сразу все объекты водном из наборов.
Вот пример. Допустим, мы работаем с четырьмя объектами ядра (см. таблицу ниже).
Объект ядра
Значение описателя
Поток
0x1111
Семафор
0x2222
Событие
0x3333
Процесс
0x4444
Инициализировав массив описателей, как показано в следующей таблице, мы сообщаем функции
WaitForMultipleExpressions приостановить вызывающий поток до тех пор, пока не освободятся поток AND семафор OR семафор AND событие AND процесс поток AND процесс.
Индекс
Значение описателя
Набор
0 0x1111 (поток 1
0x2222 (семафор 0x0000 (OR)
3 0x2222 (семафор 4
0x3333 (событие 0x4444 (процесс 0x0000 (OR)
7 0x1111 (поток 8
0x4444 (процесс)
Вы, наверное, помните, что функции
WaitForMultipleObjects нельзя передать массив описателей, число элементов в котором превышает 64 (MAXIMUM_WAIT_OBJECTS). Так вот, при использовании
WaitForMultipleExpressions массив описателей может быть гораздо больше. Однако у Вас не должно быть более 64 выражений, а в каждом — более описателей. Кроме того,
WaitForMultipleExpressions будет работать некорректно, если
Вы передадите ей хотя бы один описатель мьютекса. (Почему — объясню позже.)
Возвращаемые значения функции
WaitForMultipleExpressions показаны в следующей таблице. Если заданное выражение становится истинным,
WaitForMultipleExpressions

277
Г ЛАВА 10
Полезные средства для синхронизации потоков возвращает индекс этого выражения относительно WAIT_OBJECT_0. Если взять тот же пример, то при освобождении объектов потоки процесс
WaitForMultipleExpressions
вернет индекс в виде WAIT_OBJECT_0 + Возвращаемое значение
Описание
От Указывает, какое выражение стало истинным.
до (WAIT_OBJECT_0
+ число выражений – Ни одно выражение не стало истинным в течение заданного времени.
WAIT_FAILED
Произошла ошибка. Чтобы получить более подробную информацию, вызовите
GetLastError. Код означает, что Вы указали более 64 выражений, а ERROR_SEC
RET_TOO_LONG — что по крайней мере водном выражении указано более 63 объектов. Могут возвращаться коды и других ошибок.
Программа-пример WaitForMultExp
Эта программа, «10 WaitForMultExp.exe» (см. листинг на рис. 10 4), предназначена для тестирования функции
WaitForMultipleExpressions. Файлы исходного кода и ресурсов этой программы находятся в каталоге 10 WaitForMultExp на компакт диске, прилагаемом к книге. После запуска WaitForMultExp открывается диалоговое окно, показанное ниже.
Если Вы не станете изменять предлагаемые параметры, а просто щелкнете кнопку, диалоговое окно будет выглядеть так, как показано наследующей иллюстрации.
Программа создает четыре объекта ядра событие в занятом состоянии и помещает в многоколоночный список (с возможностью выбора сразу нескольких элементов) по одной записи для каждого объекта ядра. Далее программа анализирует содержимое поля Expression и формирует массив описателей. По умолчанию я предлагаю объекты ядра и выражение, как в предыдущем примере.
Поскольку я задал время ожидания равным 30000 мс, у Вас есть 30 секунд на внесение изменений. Выбор элемента в нижнем списке приводит к вызову
SetEvent, ко

278
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
торая освобождает объекта отказ от его выбора — к вызову
ResetEvent и соответственно к переводу объекта в занятое состояние. После выбора достаточного числа элементов (удовлетворяющего одному из выражений)
WaitForMultipleExpressions возвращает управление, ив нижней части диалогового окна показывается, какому выражению удовлетворяет Ваш выбор. Если Вы не уложитесь в 30 секунд, появится слово
«Timeout».
Теперь обсудим мою функцию
WaitForMultipleExpressions. Реализовать ее было непросто, и ее применение, конечно, приводит к некоторым издержкам. Как Вы знаете,
в Windows есть функция
WaitForMultipleObjects, которая позволяет потоку ждать по единственному AND выражению WaitForMultipleObjects(
DWORD dwObjects,
CONST HANDLE* phObjects,
BOOL fWaitAll,
DWORD Чтобы расширить ее функциональность для поддержки выражений, объединяемых, я должен создать несколько потоков — по одному на каждое такое выражение.
Каждый из этих потоков ждет в вызове
WaitForMultipleObjectsEx по единственному выражению. (Почему я использую эту функцию вместо более распространенной
WaitForMultipleObjects — станет ясно позже) Когда какое то выражение становится истинным, один из созданных потоков пробуждается и завершается.
Поток, который вызвали который породил все OR потоки, должен ждать, пока одно из OR выражений не станет истинным. Для этого он вызывает функцию
WaitForMultipleObjectsEx. В параметре dwObjects передается количество порожденных потоков (OR выражений, а параметр
phObjects указывает на массив описателей этих потоков. В параметр
fWaitAll записывается FALSE, чтобы основной поток пробудился сразу после того, как станет истинным любое из выражений. И, наконец, в параметре
dwMilliseconds передается значение, идентичное тому,
которое было указано в аналогичном параметре при вызове
WaitForMultipleExpressions.
Если в течение заданного времени ни одно из выражений не становится истинным возвращает WAIT_TIMEOUT, и это же значение возвращается функцией
WaitForMultipleExpressions. А если какое нибудь выражение становит

279
Г ЛАВА 10
Полезные средства для синхронизации потоков ся истинным,
WaitForMultipleObjectsEx возвращает индекс, указывающий, какой поток завершился. Так как каждый поток представляет отдельное выражение, этот индекс сообщает и то, какое выражение стало истинным этот же индекс возвращается и функцией
WaitForMultipleExpressions.
На этом мы, пожалуй, закончим рассмотрение того, как работает функция
WaitFor
MultipleExpressions. Но нужно обсудить еще три вещи. Во первых, нельзя допустить,
чтобы несколько OR потоков одновременно пробудились в своих вызовах
WaitFor
MultipleObjectsEx, так как успешное ожидание некоторых объектов ядра приводит к изменению их состояния (например, у семафора счетчик уменьшается на 1).
WaitFor
MultipleExpressions ждет лишь до тех пор, пока одно из выражений не станет истинным, а значит, я должен предотвратить более чем однократное изменение состояния объекта.
Решить эту проблему на самом деле довольно легко. Прежде чем порождать потоки, я создаю собственный объект семафор с начальным значением счетчика,
равным 1. Далее каждый OR поток вызывает
WaitForMultipleObjectsEx и передает ей не только описатели объектов, связанных с выражением, но и описатель этого семафора. Теперь Вы понимаете, почему в каждом наборе не может быть более 63 описателей Чтобы OR поток пробудился, должны освободиться все объекты, которые он ждет, — в том числе мой специальный семафор. Поскольку начальное значение его счетчика равно 1, более одного OR потока никогда не пробудится, и, следовательно,
случайного изменения состояния каких либо других объектов не произойдет.
Второе, на что нужно обратить внимание, — как заставить ждущий поток прекратить ожидание для корректной очистки. Добавление семафора гарантирует, что пробудится не более чем один поток, но, раз мне уже известно, какое выражение стало истинным, я должен пробудить и остальные потоки, чтобы они корректно завершились. Вызова
TerminateThread следует избегать, поэтому нужен какой то другой механизм. Поразмыслив, я вспомнил, что потоки, ждущие в тревожном состоянии, принудительно пробуждаются, когда в APC очереди появляется какой нибудь элемент.
Моя реализация
WaitForMultipleExpressions для принудительного пробуждения потоков использует
QueueUserAPC. После того как WaitForMultipleObjects, вызванная основным потоком, возвращает управление, я ставлю APC вызов в соответствующие очереди каждого из все еще ждущих OR потоков выводим все еще ждущие потоки из состояния сна чтобы они могли корректно завершиться for (dwExpNum = 0; dwExpNum < dwNumExps; dwExpNum++) {
if ((WAIT_TIMEOUT == dwWaitRet) || (dwExpNum != (dwWaitRet
WAIT_OBJECT_0))) {
QueueUserAPC(WFME_ExpressionAPC, ahThreads[dwExpNum], 0);
}
}
Функция обратного вызова,
WFME_ExpressionAPC, выглядит столь странно потому, что на самом деле от нее не требуется ничего, кроме одного прервать ожидание потока это APC функция обратного вызова WINAPI WFME_ExpressionAPC(DWORD dwData) {
// в тело функции преднамеренно не включено никаких операторов
}
Третье (и последнее) — правильная обработка интервалов ожидания. Если никакие выражения таки не стали истинными в течение заданного времени, функция

280
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
WaitForMultipleObjects, вызванная основным потоком, возвращает WAIT_TIMEOUT. В
этом случае я должен позаботиться о том, чтобы ни одно выражение больше не стало бы истинными тем самым не изменило бы состояние объектов. За это отвечает следующий код ждем, когда выражение станет TRUE или когда истечет срок ожидания dwWaitRet = WaitForMultipleObjects(dwExpNum, ahThreads, FALSE, dwMilliseconds);
if (WAIT_TIMEOUT == dwWaitRet) {
// срок ожидания истек выясняем, не стало ли какое нибудь выражение истинным, проверяя состояние семафора hsemOnlyOne dwWaitRet = WaitForSingleObject(hsemOnlyOne, 0);
if (WAIT_TIMEOUT == dwWaitRet) {
// если семафор не был переведен в свободное состояние какое то выражение дало TRUE; надо выяснить — какое dwWaitRet = WaitForMultipleObjects(dwExpNum,
ahThreads, FALSE, INFINITE);
} else {
// ни одно выражение не стало TRUE,
// и WaitForSingleObject просто отдала нам семафор dwWaitRet = Яне даю другим выражениям стать истинными за счет ожидания на семафоре. Это приводит к уменьшению счетчика семафора дои никакой OR поток не может пробудиться. Но где то после вызова функции
WaitForMultipleObjects из основного потока и обращения той к
WaitForSingleObject одно из выражений может стать истинным.
Вот почему я проверяю значение, возвращаемое
WaitForSingleObject. Если она возвращает, значит, семафор захвачен основным потоком и ни одно из выражений не стало истинным. Но если она возвращает WAIT_TIMEOUT, какое то выражение все же стало истинным, прежде чем основной поток успел захватить семафор. Чтобы выяснить, какое именно выражение дало TRUE, основной поток снова вызывает
WaitForMultipleObjects, но уже с временем ожидания, равным INFINITE; здесь все в порядке, так как я знаю, что семафор захвачен OR потоком и этот поток вот вот завершится. Теперь я должен пробудить остальные OR потоки, чтобы корректно завершить их. Это делается в цикле, из которого вызывается
QueueUserAPC (о ней я уже рассказывал).
Поскольку реализация
WaitForMultipleExpressions основана на использовании группы потоков, каждый из которых ждет на своем наборе объектов, объединяемых по, мьютексы в ней неприменимы. В отличие от остальных объектов ядра мьютек сы могут передаваться потоку во владение. Значит, если какой нибудь из моих потоков заполучит мьютекс, то по его завершении произойдет отказ от мьютекса. Вот когда Microsoft добавит в Windows API функцию, позволяющую одному потоку передавать права на владение мьютексом другому потоку, тогда моя функция
WaitFor
MultipleExpressions и сможет поддерживать мьютексы. А пока надежного и корректного способа ввести в
WaitForMultipleExpressions такую поддержку я не вижу.
1   ...   26   27   28   29   30   31   32   33   ...   68


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал