Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Можно ли использовать Mutex и Event в одной Wait-функции. Т.е вопрос в том будут ли эти объекты корректно работать?
Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Можно ли использовать Mutex и Event в одной Wait-функции. Т.е вопрос в том будут ли эти объекты корректно работать?
Можно. Будут.
Вопрос в другом — корректно ли такое действие семантически? Но это уже тебе решать.
Я знаю практическое применение такого ожидания.
Потоку извне посылают "чёрную метку" через событие (сбрасываемое вручную). Только таким способом можно гарантированно выскакивать из длительных ожиданий.
enum wait_result { wait_ok, wait_timeout, wait_stop, wait_error };
wait_result waitForSingleObject(HANDLE obj, unsigned timeout)
{
HANDLE objs[2] =
{
getThreadStopper(), // имеет более высокий приоритет: если оба объекта в сигнальном состоянии, то получим именно stop
obj
};
unsigned result = WaitForSingleObject(objs,2,timeout);
switch(result)
{
case WAIT_OBJECT_0: return wait_stop;
case WAIT_OBJECT_0+1: return wait_ok;
case WAIT_TIMEOUT: return wait_timeout;
default: return wait_error;
};
}
wait_result stopThread(HANDLE thread, unsigned timeout)
{
HANDLE stopper = getThreadStopper(thread);
if(!stopper) return wait_error;
SetEvent(stopper);
return waitForSingleObject(thread,timeout); // ждём, когда поток перейдёт в сигнальное состояние (т.е. завершится)
}
// реализации функций getThreadStopper(), getThreadStopper(HANDLE thread) - на ваше усмотрение. Можно использовать TLS.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>>Можно ли использовать Mutex и Event в одной Wait-функции. Т.е вопрос в том будут ли эти объекты корректно работать?
К>Можно. Будут. К>Вопрос в другом — корректно ли такое действие семантически? Но это уже тебе решать. К>Я знаю практическое применение такого ожидания. К>Потоку извне посылают "чёрную метку" через событие (сбрасываемое вручную). Только таким способом можно гарантированно выскакивать из длительных ожиданий.
К>
К>enum wait_result { wait_ok, wait_timeout, wait_stop, wait_error };
К>wait_result waitForSingleObject(HANDLE obj, unsigned timeout)
К>{
К> HANDLE objs[2] =
К> {
К> getThreadStopper(), // имеет более высокий приоритет: если оба объекта в сигнальном состоянии, то получим именно stop
К> obj
К> };
К> unsigned result = WaitForSingleObject(objs,2,timeout);
К> switch(result)
К> {
К> case WAIT_OBJECT_0: return wait_stop;
К> case WAIT_OBJECT_0+1: return wait_ok;
К> case WAIT_TIMEOUT: return wait_timeout;
К> default: return wait_error;
К> };
К>}
К>wait_result stopThread(HANDLE thread, unsigned timeout)
К>{
К> HANDLE stopper = getThreadStopper(thread);
К> if(!stopper) return wait_error;
К> SetEvent(stopper);
К> return waitForSingleObject(thread,timeout); // ждём, когда поток перейдёт в сигнальное состояние (т.е. завершится)
К>}
К>// реализации функций getThreadStopper(), getThreadStopper(HANDLE thread) - на ваше усмотрение. Можно использовать TLS.
К>
У меня собственно именно в таком же контексте:
Mutex — синхронизирует, а Event — выполняет отвечает корректный выход потока с освобождение всех событий.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>>Можно ли использовать Mutex и Event в одной Wait-функции. Т.е вопрос в том будут ли эти объекты корректно работать?
К>Можно. Будут. К>Вопрос в другом — корректно ли такое действие семантически? Но это уже тебе решать. К>Я знаю практическое применение такого ожидания. К>Потоку извне посылают "чёрную метку" через событие (сбрасываемое вручную). Только таким способом можно гарантированно выскакивать из длительных ожиданий.
Здравствуйте, TarasCo, Вы писали:
К>>Потоку извне посылают "чёрную метку" через событие (сбрасываемое вручную). Только таким способом можно гарантированно выскакивать из длительных ожиданий.
TC>Есть еще APC и алертабельное ожидание.
Научи, пожалуйста. Как можно осмысленно выбить поток из ожидания — т.е. не по любому критерию (пришёл сигнал завершения I/O, поставлена в очередь асинхронная процедура), а именно по флагу завершения.
Кстати, по ходу дела. Придумал, как можно облегчить себе жизнь с ожиданиями мутексов.
class stop_thread {};
bool waitOne(HANDLE obj, unsigned timeout)
{
wait_result wr = waitForSingleObject(obj,timeout); // см. вышеswitch(wr)
{
case wait_ok: return true;
case wait_timeout: return false;
case wait_error: throw logic_error("wait failed"); // или какой-нибудь ещё классcase wait_stop: throw stop_thread(); // а вот это - обязательно отдельный класс, не надо его мешать с другими исключениями
}
}
bool testStop()
{
waitOne(0,0);
return true;
}
class LockMutex : boost::noncopyable
{
HANDLE mutex;
public:
explicit LockMutex(HANDLE m) : mutex(m) { waitOne(m,INFINITE); }
~LockMutex() { ReleaseMutex(mutex); }
};
Теперь попытка захватить мутекс не потребует анализа кода возврата. Если звоночек прозвенел, то будет кинуто исключение stop_thread. Ловить его нужно в самом низу потока. Или, если уж так сильно хочется — то в других местах, но потом не забыть пробросить.
void foo()
{
try
{
.......
LockMutex(resource_sentry);
.......
for(i=0;testStop() && i<10000000;++i) { ...... }
.......
}
catch(const stop_thread& )
{
cout << "The thread is being stopped!";
throw;
}
}
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, TarasCo, Вы писали:
К>>>Потоку извне посылают "чёрную метку" через событие (сбрасываемое вручную). Только таким способом можно гарантированно выскакивать из длительных ожиданий.
TC>>Есть еще APC и алертабельное ожидание.
К>Научи, пожалуйста. Как можно осмысленно выбить поток из ожидания — т.е. не по любому критерию (пришёл сигнал завершения I/O, поставлена в очередь асинхронная процедура), а именно по флагу завершения.
Честно говоря, не очень понял фразу "осмысленно выбить из ожидания"
Нужно осмысленно выполнять операцию ожидания IMHO — какой вопрос — такой ответ
Код ожидания может быть таким:
DWORD status;
do {
status = WaitForSingleObjectEx( hEventIO, INFINITE, TRUE );
//выполнили АРСif ( status == WAIT_IO_COMPLETION )
continue; //продолжаем ожидать сигнализации hEventIO - окончания ВВbreak;
} while ( 1 );
Так, например, можно закончить поток
QueueUserAPC( StopThreadAPC, TargetThread, 0 );
А АРС может выглядеть так:
VOID CALLBACK APCProc(ULONG_PTR dwParam)
{
CancelIo( hDev ); //отменяем операцию - в результате будет сигнализировано событие hEventIO
}
К>Кстати, по ходу дела. Придумал, как можно облегчить себе жизнь с ожиданиями мутексов. К>
К>class stop_thread {};
К>bool waitOne(HANDLE obj, unsigned timeout)
К>{
К> wait_result wr = waitForSingleObject(obj,timeout); // см. выше
К> switch(wr)
К> {
К> case wait_ok: return true;
К> case wait_timeout: return false;
К> case wait_error: throw logic_error("wait failed"); // или какой-нибудь ещё класс
К> case wait_stop: throw stop_thread(); // а вот это - обязательно отдельный класс, не надо его мешать с другими исключениями
К> }
К>}
К>bool testStop()
К>{
К> waitOne(0,0);
К> return true;
К>}
К>class LockMutex : boost::noncopyable
К>{
К> HANDLE mutex;
К>public:
К> explicit LockMutex(HANDLE m) : mutex(m) { waitOne(m,INFINITE); }
К> ~LockMutex() { ReleaseMutex(mutex); }
К>};
К>
К>Теперь попытка захватить мутекс не потребует анализа кода возврата. Если звоночек прозвенел, то будет кинуто исключение stop_thread. Ловить его нужно в самом низу потока. Или, если уж так сильно хочется — то в других местах, но потом не забыть пробросить. К>
Вы УВЕРЕНЫ, что это облегчит жизнь?
По делу:
Если говорить о виндах, ожидание мьютекса может вернуть еще WAIT_ABANDONED — это нормальный ход дела. Ваш код возбудт исключение, что нехорошо....
ПРавда я не нашел реализацию waitForSingleObject — может это в ней предусмотрено?
Здравствуйте, Кодт, Вы писали:
К>Научи, пожалуйста. Как можно осмысленно выбить поток из ожидания — т.е. не по любому критерию (пришёл сигнал завершения I/O, поставлена в очередь асинхронная процедура), а именно по флагу завершения.
Например:
while not Flag and
WaitForSingleObjectsEx(hMutex, INFINITE, TRUE) <> .. do ;
Где Flag устанавливается той самой, вызываемой через APC функцией.
Здравствуйте, TarasCo, Вы писали:
TC>По делу: TC>Если говорить о виндах, ожидание мьютекса может вернуть еще WAIT_ABANDONED — это нормальный ход дела. Ваш код возбудт исключение, что нехорошо....
Нельзя сказать, что очень нормальный ход дела... Если для захвата используются классы наподобие LockMutex, то такая ситуация сигнализирует о больших проблемах в логике программы.
TC> ПРавда я не нашел реализацию waitForSingleObject — может это в ней предусмотрено?
Естественно, её можно переписать на свой вкус. В моих программах она вообще с хэндлами дела не имеет, а только с обёртками — поэтому там коды возврата устроены по-другому. Здесь я привёл эскиз.
Здравствуйте, TarasCo, Вы писали:
TC>Вы УВЕРЕНЫ, что это облегчит жизнь?
Уверен. Если у потоков в программе сделать универсальную функцию запуска, которая
— регистрирует флаг остановки
— ловит все исключения из действительной функции потока
то в большинстве случаев команда stopThread безопасным способом прибьёт поток с минимальными затратами со стороны программиста.
В тех случаях, когда поток должен перед смертью выполнить танец с бубном — пожалуйста, пишем catch. Это не намного заковыристее, чем написать
Минус — в том, что обработчик исключения не может сам кидать исключения. Но это ограничение, если оно является препятствием в конкретном месте, тоже можно обойти.
Отсюда вопрос: что лучше, ловить (одним или вторым способом) исключения там, где обработка "последнего звонка" нетривиальна, или же писать проверки и быстрые обходные пути повсеместно, где есть длительные операции?
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, TarasCo, Вы писали:
TC>>По делу: TC>>Если говорить о виндах, ожидание мьютекса может вернуть еще WAIT_ABANDONED — это нормальный ход дела. Ваш код возбудт исключение, что нехорошо....
К>Нельзя сказать, что очень нормальный ход дела... Если для захвата используются классы наподобие LockMutex, то такая ситуация сигнализирует о больших проблемах в логике программы.
Очень даже нормальный ход. Мьютексы используюия обычно при работе с несколькими процессами ( иначе следует использовать критические секции ). Соответсвенно, не следует предполагать, что другой процесс корректно завершиться — его могут, например, прибить через TerminateThread — в этом случае другому ожидающиму процессу предоставится случай захватить мьютекс — он же в место этого сам упадет — "принцип домино"
Здравствуйте, TarasCo, Вы писали:
TC>Очень даже нормальный ход. Мьютексы используюия обычно при работе с несколькими процессами ( иначе следует использовать критические секции ). Соответсвенно, не следует предполагать, что другой процесс корректно завершиться — его могут, например, прибить через TerminateThread — в этом случае другому ожидающиму процессу предоставится случай захватить мьютекс — он же в место этого сам упадет — "принцип домино"
Да, понял.
Ну что же, нет проблем.
Поскольку WaitFor... возвращает WAIT_OBJECT_0+n либо WAIT_ABANDONED+n (где n — номер синхрообъекта в перечне), можно обрабатывать их так:
— WAIT_OBJECT_0 + 0 — подняли флаг завершения
— WAIT_ABANDONED_0 + 0 — прибили флаг завершения; поскольку предполагается, что это локальное событие, то либо завершается главный поток (тем более пора сваливать), либо программа разрушена
— WAIT_{OBJECT|ABANDONED}_0 + n, n=[1;N] — что-то интересное произошло с пользовательскими синхрообъектами соответствующий код
— WAIT_TIMEOUT, WAIT_OBJECT_0 + N+1, WAIT_IO_COMPLETION — соответственно, таймаут, сообщение (если это MsgWait...) и завершение ВВ (если это Wait...Ex)
Можно или оставить коды как есть, или отображать в специальный тип.