Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

NOTE:

В Windows 2000 и Windows NT 4 в этом параметре можно передавать NULL (обычно так и делается). Тем самым Вы сообщаете функции, что Вас не инте ресует идентификатор потока Ilo в Windows 95/98 это приведет к ошибке, так как функция попытается записать идентификатор потока no нулевому адресу, что недопустимо. И поток не будет создан.

Такое несоответствие между операционными системами может создать разработчикам приложений массу проблем, Допустим, Вы пишете и тестируе те программу в Windows 2000 (которая создает поток, даже если Вы передаете NULL в pdwThreadID) Но вот Вы запускаете приложение в Windows 98, и фун кция CreateThread, естественно, дает ошибку. Вывод один: тщательно тестируй те свос приложение во всех операционных системах, в которых оно будет работать

Завершение потока

Поток можно завершить четырьмя способами:

функция потока возвращает управление (рекомендуемый способ),

поток самоуничтожяется вызовом функции ExitThread (нежелательный способ); один из потоков данного или стороннего процесса вызывает функцию Termi nateThread (нежелательный способ);

завершается процесс, содержащий данный поток (тоже нежелательно).

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

Возврат управления функцией потока

Функцию потока следует проектировать так, чтобы поток завершался только после того, как она возвращает управление. Это единственный способ, гарантирующий кор ректную очистку всех ресурсов, принадлежавших Вашему потоку. При этом:

любые С++-объекты, созданные данным потоком, уничтожаются соответству ющими деструкторами; система корректно освобождает память, которую занимал стек потока;

система устанавливает код завершения данного потока (поддерживаемый объ ектом ядра "поток») — его и возвращает Ваша функция потока; счетчик пользователей данного объекта ядра "поток" уменьшается на 1

Функция ExitThread

Поток можно завершить принудительно, вызвав:

VOID ExitThread(DWORD dwExitCоde);

При этом освобождаются все ресурсы операционной системы, выделенные дан ному потоку, но C/C++ - pеcypcы (например, объекты, созданные из С++-классов) не очищаются Именно поэтому лучше возвращать управление из функции потока, чем самому вызывать функцию ExitThread. (Подробнее на эту тему см. раздел "Функция

ExitProcess» в главе 4.)

В параметр dwExitCode Вы помещаете значение, которое система рассматривает как код завершения потока. Возвращаемого значения у этой функции нет, потому что после ее вызова поток перестает существовать.

NOTE:

ExitThread — это Windows-функция, которая уничтожает поток. Но никогда не вы зывайте ее, если Вы пишете код на С/С++ Вместо нее Вы должны использовать функцию _endthreadex из библиотеки Visual С++ (Если Вы работаете с другим компилятором, он должен поддерживать свой эквивалент функции ExitThread) Что именно делает _endthreadex и почему это так важно, и объясню потом.

Функция TerminateThread

Вызов этой функции также завершает поток:

BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);

В отличие от ExitThread, которая уничтожает только вызывающий поток, эта фун кция завершает поток, указанный в параметре hThread. В параметр dwExitCode Вы помещаете значение, которое система рассматривает как код завершения потока. После того как поток будет уничтожен, счетчик пользователей его объекта ядра "по ток» уменьшится на 1

NOTE:

TerminateThread — функция ясинхронная, т, e. она сообщает системе, что Вы хотите завершить поток, но к тому времени, когда она вернет управление, поток может быть еще не уничтожен. Так что, если Вам нужно точно знать момент завершения потока, используйте WaitForSingleObject (см. главу 9) или аналогичную функцию, передав ей описатель этого потока

Корректно написанное приложение не должно вызывать эту функцию, поскольку поток не получает никакого уведомления о завершении; из-за этого он не может вы полнить должную очистку ресурсов.

NOTE:

Уничтожение потока при вызове ExitThread или возврате управления из функ ции потока приводит к разрушению его стека. Но если он завершен функцией TerminateThread, система не уничтожает стек, пока не завершится и процесс, которому принадлежал этот поток Так сделано потому, что другие потоки могут использовать указатели, ссылающиеся на данные в стеке завершенного потока. Если бы они обратились к несуществующему стеку, произошло бы на рушение доступа

Кроме того, при завершении потока система уведомляет об этом все DLL, подключенные к процессу — владельцу завершенного потока. Но при вызове TetminateThread такого не происходит, и процесс может быть завершен некор ректно (Подробнее на этутему см. главу 20.)

Если завершается процесс

Функции ExitProcess и TerminateProcess, рассмотренные в главе 4, тоже завершают потоки. Единственное отличие в том, что они прекращают выполнение всех потоков, принадлежавших завершенному процессу При этом гарантируется высвобождение любых выделенных процессу ресурсов, в том числе стеков потоков Однако эти две функции уничтожают потоки принудительно — так, будто для каждого из них вызы вается функция TerminateThread. А это означает, что очистка проводится некоррект но, деструкторы С++-объектов не вызываются, данные на диск не сбрасываются и т д