Windows: При каких обстоятельствах SetEvent () может не возвращаться немедленно? - PullRequest
1 голос
/ 22 ноября 2008

У меня есть поток, который, когда его функция выходит из цикла (выход запускается событием), выполняет некоторую очистку, а затем устанавливает другое событие, чтобы главный поток знал, что это сделано.

Однако при некоторых обстоятельствах SetEvent (), по-видимому, не возвращается после того, как он установил событие потока «Я закончил».

Этот поток является частью DLL, и проблема возникает после того, как DLL была загружена / присоединена, поток запущен, поток завершен, а DLL несколько раз отключалась / выгружалась без остановки приложения между ними. Число повторений этой последовательности до возникновения этой проблемы является переменным.

В случае, если вы скептически относитесь к тому, что я знаю, о чем я говорю, я определил, что происходит, заключив вызов SetEvent () в скобки с вызовами OutputDebugString (). Вывод до SetEvent () появляется. Затем ожидающий поток выдает вывод, который указывает, что событие установлено.

Тем не менее, второй вызов OutputDebugString () в выходящем потоке (один после ПОСЛЕ SetEvent ()) никогда не происходит, или, по крайней мере, его строка никогда не обнаруживается. Если это произойдет, приложение вылетает через несколько секунд.

(Обратите внимание, что вызовы OutputDebugString () были добавлены после того, как проблема начала возникать, поэтому она вряд ли будет зависать там, а не в SetEvent ().)

Я не совсем уверен, что вызывает сбой, но это происходит в том же потоке, в котором SetEvent () не возвращался немедленно (я отслеживал / выводил идентификаторы потоков). Я предполагаю, что вполне возможно, что SetEvent () наконец-то возвращается, после чего контекст, к которому он возвращается, утерян / недействителен, но что может вызвать такую ​​задержку?

Оказывается, я был ослеплен, глядя на этот код так долго, и мне даже не пришло в голову проверить код возврата. Я посмотрел на него сегодня, поэтому я буду знать, что он возвращает (, если возвращается) в понедельник, и тогда я отредактирую этот вопрос с этой информацией.

Обновление: я изменил (основной) код, чтобы дождаться завершения потока, а не для установки события, и удалил вызов SetEvent () из подчиненного потока. Это изменило природу ошибки: теперь, вместо того, чтобы не вернуться из SetEvent (), он вообще не выходит из потока и все зависает.

Это указывает на то, что проблема не в SetEvent (), а в чем-то более глубоком. Пока понятия не имею, но хорошо не преследовать этот тупик.

Обновление (13 февраля 2009 г.):
Оказалось, что проблема была глубже, чем я думал, когда задавал этот вопрос. jdigital (и, возможно, другие) в значительной степени преуменьшил основную проблему: мы пытались выгрузить поток как часть процесса отключения DLL.

Это, как я не осознавал в то время, но с тех пор выяснил это благодаря исследованиям здесь и в другом месте (например, в блоге Рэймонда Чена), это очень плохая вещь.

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

Некоторые из предложений здесь помогли мне сделать это, поэтому я благодарен всем, кто внес свой вклад. Спасибо!

Ответы [ 4 ]

2 голосов
/ 22 ноября 2008

Вы разыменовываете HANDLE *, чтобы перейти к SetEvent? Скорее всего, ссылка на дескриптор события недействительна, а сбой является нарушением прав доступа (т. Е. Доступ к мусору).

1 голос
/ 22 ноября 2008

Кто выгружает DLL и в какое время выполняется выгрузка? Мне интересно, есть ли здесь проблема с синхронизацией, когда DLL выгружается до завершения потока.

0 голосов
/ 22 ноября 2008

Зачем вам нужно устанавливать событие в подчиненном потоке, чтобы инициировать для основного потока выполнение этого потока? просто выйдите из потока, вызывающий главный поток должен дождаться завершения рабочего потока, например, псевдокод -

Master
{
   TerminateEvent = CreateEvent ( ... ) ;
   ThreadHandle = BeginThread ( Slave, (LPVOID) TerminateEvent ) ;
   ...
   Do some work
   ...
   SetEvent ( TerminateEvent ) ;
   WaitForSingleObject ( ThreadHandle, SOME_TIME_OUT ) ;
   CloseHandle ( TerminateEvent ) ;
   CloseHandle ( ThreadHandle ) ; 
}

Slave ( LPVOID ThreadParam )
{
   TerminateEvent = (HANDLE) ThreadParam ;
   while ( WaitForSingleObject ( TerminateEvent, SOME__SHORT_TIME_OUT ) == WAIT_TIMEOUT )
   { 
      ... 
      Do some work 
      ...
   }
}

Существует множество состояний ошибок и состояний, которые необходимо проверить, но это суть того, как я обычно это делаю.

Если вы сможете достать ее, достаньте эту книгу, она изменила мою жизнь в отношении разработки Windows, когда я впервые прочитал ее много, много лет назад.

Расширенные возможности Windows: Руководство разработчика по Win32 Api для Windows Nt 3.5 и Windows 95 (мягкая обложка), автор Jeffrey Richter (Author)

0 голосов
/ 22 ноября 2008

Возможно, вы захотите использовать WinDbg для обнаружения сбоя и проверки стека.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...