Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции-ТРПС.doc
Скачиваний:
12
Добавлен:
15.11.2018
Размер:
810.5 Кб
Скачать

12.3. Создание рабочего потока

В первую очередь нужно написать так называемую “функцию рабочего потока”, тело которой и содержит операции, выполняемые рабочим потоком. Прототип функции одинаков для всех потоков

DWORD WINAPI ThreadFunc (LPVOID pData);

Имя функции рабочего потока не имеет значения, поскольку, как и для оконной процедуры, Windows использует ее адрес, а не имя. Все функции рабочего потока имеют только один параметр типа LPVOID. Следовательно, чтобы передать в функцию несколько параметров из вызывающего процесса, нужно определить собственную структуру, заполнить поля и передать ее адрес через pData.

Особенность функции рабочего потока состоит в том, что при выходе из нее поток автоматически завершается.

Используйте именно этот путь для окончания рабочего потока, а не принудительное завершение через специальные функции Win32 API. Это может быть источником трудноуловимых ошибок межпотокового взаимодействия.

Непосредственное создание рабочего потока происходит в момент вызова Win32 API функции CreateThread(), которая имеет прототип:

HANDLE CreateThread( SECURITY_ATTRIBUTES* pThreadAttr, DWORD dwStackSize, THREAD_START_ROUTINE* pThreadFunc, LPVOID pData, DWORD dwFlags, LPDWORD pIdThread);

Первый параметр обычно не используется и равен NULL, второй – определяет размер стека для рабочего потока. Если он равен 0, Windows сама определяет необходимый размер. Третий параметр – это указатель на функцию рабочего потока, а четвертый – указатель блока памяти, который будет передан в ThreadFunc(). Флаг активизации рабочего потока обычно равен 0, что означает немедленный запуск потока. Через последний указатель буден возвращен идентификатор созданного потока. Однако больший интерес представляет дескриптор потока, возвращаемый функцией CreateThread(). Он позволяет вызывающему процессу проводить некоторые манипуляции с запущенными потоками.

Ниже представлен фрагмент кода, выполняющий запуск рабочего потока:

HANDLE hThread; DWORD idThread; THREAD_PARAM params; // .... hThread = ::CreateThread(NULL, 0, ThreadFunc, &params, 0, &idThread); if (!hThread) { // ошибки создания потока }

Здесь THREAD_PARAM – некоторая структура, которая определяется в приложении. Она служит для передачи набора параметров в функцию потока.

Этот фрагмент содержит потенциальную ошибку, суть которой объяснена ниже.

При выходе из блока, запустившего поток, происходит разрушение локальных переменных params и hThread. Но уже запущенный поток не может “знать” об этом, он продолжает пользоваться данными из блока памяти, которого уже нет! Последствия этого – непредсказуемы.

Дескриптор hThread необходим для корректного освобождения ресурсов потока после его завершения следующим образом:

CloseHandle(hThread);

Выходом из сложившейся ситуации является объявление параметров params и hThread как глобальные или статические переменные.

12.4. Организация взаимодействия потоков

Как уже отмечалось, главная трудность технологии многопотокового программирования – это организация взаимодействия потоков. Приложение должно иметь возможность дуплексного обмена информацией. Это означает, что данные должны передаваться как от рабочего потока к процессу или, другими словами, в главный поток, так и в обратном направлении – от процесса в рабочий поток.