Mutex и Event
От: AcidTheProgrammer Россия https://hts.tv/
Дата: 15.08.05 05:58
Оценка:
Можно ли использовать Mutex и Event в одной Wait-функции. Т.е вопрос в том будут ли эти объекты корректно работать?
Re: Mutex и Event
От: serg_fork  
Дата: 15.08.05 06:07
Оценка: 2 (1)
Здравствуйте, AcidTheProgrammer, Вы писали:

ATP>Можно ли использовать Mutex и Event в одной Wait-функции. Т.е вопрос в том будут ли эти объекты корректно работать?


Еще как будут. Можно даже процесс и событие : )
Re: Mutex и Event
От: Кодт Россия  
Дата: 15.08.05 09:01
Оценка:
Здравствуйте, 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.
Перекуём баги на фичи!
Re[2]: Mutex и Event
От: AcidTheProgrammer Россия https://hts.tv/
Дата: 15.08.05 09:09
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, 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 — выполняет отвечает корректный выход потока с освобождение всех событий.
Re[2]: Mutex и Event
От: TarasCo  
Дата: 15.08.05 09:43
Оценка: 21 (1)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, AcidTheProgrammer, Вы писали:


ATP>>Можно ли использовать Mutex и Event в одной Wait-функции. Т.е вопрос в том будут ли эти объекты корректно работать?


К>Можно. Будут.

К>Вопрос в другом — корректно ли такое действие семантически? Но это уже тебе решать.
К>Я знаю практическое применение такого ожидания.
К>Потоку извне посылают "чёрную метку" через событие (сбрасываемое вручную). Только таким способом можно гарантированно выскакивать из длительных ожиданий.

Есть еще APC и алертабельное ожидание.
Да пребудет с тобою сила
Re[3]: Mutex и Event
От: Кодт Россия  
Дата: 15.08.05 10:14
Оценка:
Здравствуйте, 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;
  }
}
Перекуём баги на фичи!
Re[4]: Mutex и Event
От: TarasCo  
Дата: 15.08.05 10:59
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, 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. Ловить его нужно в самом низу потока. Или, если уж так сильно хочется — то в других местах, но потом не забыть пробросить.
К>
К>void foo()
К>{
К>  try
К>  {
К>    .......
К>    LockMutex(resource_sentry);
К>    .......
К>    for(i=0;testStop() && i<10000000;++i) { ...... }
К>    .......
К>  }
К>  catch(const stop_thread& )
К>  {
К>    cout << "The thread is being stopped!";
К>    throw;
К>  }
К>}
К>


Вы УВЕРЕНЫ, что это облегчит жизнь?
По делу:
Если говорить о виндах, ожидание мьютекса может вернуть еще WAIT_ABANDONED — это нормальный ход дела. Ваш код возбудт исключение, что нехорошо....
ПРавда я не нашел реализацию waitForSingleObject — может это в ней предусмотрено?
Да пребудет с тобою сила
Re[4]: Mutex и Event
От: Leonid Troyanovsky  
Дата: 15.08.05 11:04
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Научи, пожалуйста. Как можно осмысленно выбить поток из ожидания — т.е. не по любому критерию (пришёл сигнал завершения I/O, поставлена в очередь асинхронная процедура), а именно по флагу завершения.


Например:

while not Flag and
WaitForSingleObjectsEx(hMutex, INFINITE, TRUE) <> .. do ;

Где Flag устанавливается той самой, вызываемой через APC функцией.
--
С уважением, LVT
Re[5]: Mutex и Event
От: Кодт Россия  
Дата: 15.08.05 11:23
Оценка:
Здравствуйте, TarasCo, Вы писали:

TC>По делу:

TC>Если говорить о виндах, ожидание мьютекса может вернуть еще WAIT_ABANDONED — это нормальный ход дела. Ваш код возбудт исключение, что нехорошо....

Нельзя сказать, что очень нормальный ход дела... Если для захвата используются классы наподобие LockMutex, то такая ситуация сигнализирует о больших проблемах в логике программы.

TC> ПРавда я не нашел реализацию waitForSingleObject — может это в ней предусмотрено?


Этажом выше по ветке. http://www.rsdn.ru/Forum/Message.aspx?mid=1326709&amp;only=1
Автор: Кодт
Дата: 15.08.05

Естественно, её можно переписать на свой вкус. В моих программах она вообще с хэндлами дела не имеет, а только с обёртками — поэтому там коды возврата устроены по-другому. Здесь я привёл эскиз.
Перекуём баги на фичи!
Re[5]: Mutex и Event
От: Кодт Россия  
Дата: 15.08.05 11:38
Оценка:
Здравствуйте, TarasCo, Вы писали:

TC>Вы УВЕРЕНЫ, что это облегчит жизнь?


Уверен. Если у потоков в программе сделать универсальную функцию запуска, которая
— регистрирует флаг остановки
— ловит все исключения из действительной функции потока
то в большинстве случаев команда stopThread безопасным способом прибьёт поток с минимальными затратами со стороны программиста.
В тех случаях, когда поток должен перед смертью выполнить танец с бубном — пожалуйста, пишем catch. Это не намного заковыристее, чем написать
some foo()
{
  do_something();
  if(!ThisThreadIsOver()) do_something_more();
  if(!ThisThreadIsOver()) and_more();
  if(!ThisThreadIsOver()) return all_right;

  do_stop();
  return please_stop_me;
}

// versus

some foo()
{
  try
  {
    do_something();
    testStop();
    do_something_more();
    testStop();
    and_more();
    testStop();
    return all_right;
  }
  catch(stop_thread const&) // если do_stop() - нетривиально
  {
    do_stop();
    throw;
  }
}

Минус — в том, что обработчик исключения не может сам кидать исключения. Но это ограничение, если оно является препятствием в конкретном месте, тоже можно обойти.
some foo()
{
  try
  {
    ........
    return all_right;
  }
  catch(stop_thread const&) {}

  do_stop();
  throw stop_thread();
}

Отсюда вопрос: что лучше, ловить (одним или вторым способом) исключения там, где обработка "последнего звонка" нетривиальна, или же писать проверки и быстрые обходные пути повсеместно, где есть длительные операции?
Перекуём баги на фичи!
Re[6]: Mutex и Event
От: TarasCo  
Дата: 15.08.05 11:51
Оценка: 21 (1)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, TarasCo, Вы писали:


TC>>По делу:

TC>>Если говорить о виндах, ожидание мьютекса может вернуть еще WAIT_ABANDONED — это нормальный ход дела. Ваш код возбудт исключение, что нехорошо....

К>Нельзя сказать, что очень нормальный ход дела... Если для захвата используются классы наподобие LockMutex, то такая ситуация сигнализирует о больших проблемах в логике программы.


Очень даже нормальный ход. Мьютексы используюия обычно при работе с несколькими процессами ( иначе следует использовать критические секции ). Соответсвенно, не следует предполагать, что другой процесс корректно завершиться — его могут, например, прибить через TerminateThread — в этом случае другому ожидающиму процессу предоставится случай захватить мьютекс — он же в место этого сам упадет — "принцип домино"
Да пребудет с тобою сила
Re[7]: Mutex и Event
От: Кодт Россия  
Дата: 15.08.05 12:12
Оценка:
Здравствуйте, 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)
Можно или оставить коды как есть, или отображать в специальный тип.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.