К Т П           План занятия                                                              1                                           Страницы  | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 |

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

Поток завершается вызовом функции ExitThread, которая имеет следующий прототип:
VOID ExitThread (
DWORD dwExitCode        // код завершения потока

);

Эта функция может вызываться как явно, так и неявно при возврате значения из функции потока. При выполнении этой функции система посылает динамическим библиотекам, которые загружены процессом, сообщение DLL_THREAD_DETACH, которое говорит о том, что поток завершает свою работу.
Если поток создается при помощи макрокоманды _beginthreadex, то для завершения потока нужно использовать макрокоманду _endthreadex, единственным параметром которой является код возврата из потока. Эта макрокоманда описана в заголовочном файле process.h. Причина использования в этом случае макрокоманды _endthreadex заключается в том, что она не только выполняет выход из потока, но и освобождает память, которая была распределена макрокомандой _beginthreadex. Если поток создан функцией _beginthreadex, то для выхода из потока функция _endthreadex может вызываться как явно, так и неявно при возврате значения из функции потока.

Один поток может завершить другой поток, вызвав функцию
TerminateThread, которая имеет следующий прототип:
BOOL TerminateThread (
HANDLE hThread,         // дескриптор потока
DWORD dwExitThread      // код завершения потока
);

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

В листинге 3.3 приведена программа, которая демонстрирует работу функции TerminateThread. В этой программе следует обратить внимание на квалификатор типа volatile, который указывает компилятору, что значение переменной count должно храниться в памяти, т. к. к этой переменной имеют доступ параллельные потоки. Дело в том, что сам компилятор языка программирования С или C++ не знает, что такое поток. Для него это просто функция. А в языках программирования С и С++ любая функция вызывается только синхронно, т. е. функция, вызвавшая другую функцию, ждет завершения этой функции. Если не использовать квалификатор volatile, то компилятор может оптимизировать код и в одном потоке хранить значение переменной в регистре, а в другом потоке — в оперативной памяти. В результате параллельно работающие потоки будут обращаться к разным переменным.

Листинг 3.3. Завершение потока функцией TerminateThread

#include <windows.h>
#include <iostream.h>

volatile UINT count;

void thread()
{
for (;;)
{
++count;
Sleep(100); // немного отдохнем
}
}

int main()
{
HANDLE hThread;
DWORD IDThread;
char c;

hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread, NULL,
0, &IDThread);
if (hThread == NULL)
return GetLastError();

for (;;)
{
cout << "Input 'y' to display the count or any char to finish: ";
cin >> c;
if (c == 'y')
cout << "count = " << count << endl;
else
break;
}

// прерываем выполнение потока thread
TerminateThread(hThread, 0);

// закрываем дескриптор потока
CloseHandle(hThread);

return 0;
}

 


Предыдущая        В начало страницы       Следующая
3