Параметры pfnStartAddr и pvParam
Параметр pfnStartAddr определяет адрес функции потока, с которой должен будет начять работу создаваемый поток, а параметр pvParam идентичен параметру рvРаrаm функции потока. CreateTbread лишь передает этот параметр по эстафете той функции, с которой начинается выполнение создаваемого потока. Таким образом, данный параметр позволяет передавать функции потока какое-либо инициализирующее значение. Оно может быть или просто числовым значением, или указателем на структуру данных с дополнительной информацией.
Вполне допустимо и даже полезно создавать несколько потоков, у которых в качестве входной точки используется адрес одной и той же функции. Например, можно реализовать Web-сервер, который обрабатывает каждый клиентский запрос в отдельном потоке. При создании каждому потоку передается свое значение рvParam.
Учтите, что Windows — операционная система с вытесняющей многозадачностью, а следовательно, новый поток и поток, вызвавший CreateThread, могут выполняться одновременно. В связи с этим возможны проблемы. Остерегайтесь, например, такого кода.
DWORD WINAPI FirstThread(PVOID pvParam)
{
// инициализируем переменную, которая содержится в стеке
int x = 0;
DWORD dwThreadID;
// создаем новый поток
HANDLE hThread = CreateThread(NULL, 0, SecondThread, (PVOID) &x, 0, &dwThreadId);
// мы больше не слылаемся на новый поток,
// поэтому закрываем свой описатель этого потока
CloseHandle(hThread);
// Наш поток закончил работу.
// ОШИБКА, его стек будет разрушен, но SecondThread // может попытаться обратиться к нему return(0);
}
DWORD WINAPI SecondThread(PVOID pvParam) {
// здесь выполняется какая-то длительная обработка
// Пытаемся обратиться к переменной в стеке FirstThread,
// ПРИМЕЧАНИЕ- это может привести к ошибке общей защиты
// нарушению доступа * ((int *) pvParam) = 5;
relurn(0);
}
Не исключено, что в приведенном коде FirstThread закончит свою работу до того, как SecondThread присвоит значение 5 переменной x из FirstThread. Если так и будет, SecondThread не узнает, что FirstThread больше не существует, и попытается изменить содержимое какого-то участка памяти с недействительным теперь адресом. Это неизбежно вызовет нарушение доступа: стек первого потока уничтожен по завершении.
FirstThread. Что же делать? Можно объявить x статической переменной, и компилятор отведет память для хранения переменной x не в стеке, а в разделе данных приложения (application's data section). Ho тогда функция станет нереентерабельной. Иначе говоря, в этом случае Вы не смогли бы создать два потока, выполняющих одну и ту же функцию, так как оба потока совместно использовали бы статическую переменную. Другое решение этой проблемы (и его более сложные варианты) базируется па методах синхронизации потоков, речь о которых поЙдет в главах 8, 9 и 10.