Выполнение процедур на многопроцессорных ВС

  • Автор темы Programmer_Hard
  • Дата начала
P

Programmer_Hard

#1
всем здравствуйте
Столкнулся с такой задачей:
Нужно выполнить процедуру сортировки ( да в принципе не важно какую) на многопроцессорной машине. Естественноно нужно распараллелить ее на несколько процессов , чтобы каждый проц выполнял свой процес(поток), чтобы выжать из системы максимум.
А вот как определить сколько ядер на машине не знаю.
Может кто подскажет чего-нибудь по теме?
 
04.09.2006
2 566
3
#2
А тебе это надо? Зачем привязываться к количеству ядер/процессоров?
 
P

Programmer_Hard

#3
Мне Нужно создать класс ,сортирующий массив, который бы создавал n (n-число процессоров) процессов, каждый из который бы работал только с куском массива передаваемым ему как параметр.

Каким образом можно создавать потоки в своём классе ?
 
B

Barmutik

#6
Я бы посоветовал создавать не процессы а потоки и потом уже в зависимости от числа процессоров в системе распеределятьих непосредственно на разные процессоры... Хотя можно и процессы.. только тогда придётся организовывать межпроцессной взаимодействие.. нампример через Shared Memory...

Вы только учтите сразу что Борляндовский менеджер памяти ОЧЕНЬ не дружит с многопроцессорностью и потенциально Вы можете получить замедление работы а не выигрыш по скорости. Хотя конечно это сильно зависит от того как Вы организуете Ваши структуры данных...
 
P

Programmer_Hard

#7
Можно ли использовать функцию CreateThread() и как для создания потока?
Примерная структура класса:
Код:
class Parallel{
int n; //chislo potokov
public:
Parallel(){n=0;};
My *Potok[16];
void add_potok();
int *Run(int *mas,int n_mas);
};

void Parallel::add_potok(){
Potok[n]=CreateThread(/*  какой использовать параметр ?  */);
n++;
}
 
04.09.2006
2 566
3
#8
<!--QuoteBegin-Programmer_Hard+20:03:2007, 09:04 -->
<span class="vbquote">(Programmer_Hard @ 20:03:2007, 09:04 )</span><!--QuoteEBegin-->Можно ли использовать функцию CreateThread() и как для создания потока?
[snapback]59545" rel="nofollow" target="_blank[/snapback]​
[/quote]

Функция CreateThread
Мы уже говорили, как при вызове функции CreateProcess появляется на свет первичный поток процесса. Если Вы хотите создать дополнительные потоки, нужно вызывать из первичного потока функцию CreateThread:

HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa,
DWORD cbStack,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD fdwCreate,
PDWORD pdwThreadID);
При каждом вызове этой функции система создает объект ядра "поток". Это не сам поток, а компактная структура данных, которая используется операционной системой для управления потоком и хранит статистическую информацию о потоке. Так что объект ядра "поток" — полный аналог объекта ядра "процесс".

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

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

Параметр psa
Параметр psa является указателем на структуру SECURITY_ATTRIBUTES. Если Вы хотите, чтобы объекту ядра "поток" были присвоены атрибуты защиты по умолчанию (что чаще всего и бывает), передайте в этом параметре NULL. A чтобы дочерние процессы смогли наследовать описатель этого объекта, определите структуру SECURITY_ATTRIBUTES и инициализируйте ее элемент bInheritHandle значением TRUE (см. главу 3).

Параметр cbStack
Этот параметр определяет, какую часть адресного пространства поток сможет использовать под свой стек. Каждому потоку выделяется отдельный стек. Функция CreateProcess, запуская приложение, вызывает CreateThread, и та инициализирует первичный поток процесса. При этом CreateProcess заносит в параметр cbStack значение, хранящееся в самом исполняемом файле. Управлять этим значением позволяет ключ /STACK компоновщика:

/STACK.[reserve][,commit]

Аргумент reserve определяет объем адресного пространства, который система должна зарезервировать под стек потока (по умолчанию — 1 Мб). Аргумент commit задает объем физической памяти, который изначально передается области, зарезервированной под стек (по умолчанию — 1 страница). По мере исполнения кода в потоке Вам, весьма вероятно, понадобится отвести под стек больше одной страницы памяти. При переполнении стека возникнет исключение. (О стеке потока и исключениях, связанных с его переполнением, см. главу 16, а об общих принципах обработки исключений — главу 23.) Перехватив это исключение, система передаст зарезервированному пространству еще одну страницу (или столько, сколько указано в аргументе commit). Такой механизм позволяет динамически увеличивать размер стека лишь по необходимости.

Если Вы, обращаясь к CreateThread, передаете в параметре cbStack ненулевое значение, функция резервирует всю указанную Вами память. Ее объем определяется либо значением параметра cbStack, либо значением, заданным в ключе /STACK компоновщика (выбирается большее из них). Но передается стеку лишь тот объем памяти, который соответствует значению в cbStack. Если же Вы передаете в параметре cbStack нулевое значение, CreateThread создает стек для нового потока, используя информацию, встроенную компоновщиком в EXE-файл.

Значение аргумента reserve устанавливает верхний предел для стека, и это ограничение позволяет прекращать деятельность функций с бесконечной рекурсией. Допустим, Вы пишете функцию, которая рекурсивно вызывает сама себя. Предположим также, что в функции есть "жучок", приводящий к бесконечной рекурсии. Всякий раз, когда функция вызывает сама себя, в стеке создается новый стековый фрейм. Если бы система не позволяла ограничивать максимальный размер стека, рекурсивная функция так и вызывала бы сама себя до бесконечности, а стек поглотил бы все адресное пространство процесса. Задавая же определенный предел, Вы, во-первых, предотвращаете разрастание стека до гигантских объемов и, во-вторых, гораздо быстрее узнаете о наличии ошибки в своей программе. (Программа-пример Summation в главе 16 продемонстрирует, как перехватывать и обрабатывать переполнение стека в приложениях.)

Параметры pfnStartAddr и pvParam
Параметр pfnStartAddr определяет адрес функции потока, с которой должен будет начать работу создаваемый поток, а параметр pvParam идентичен параметру pvParam функции потока. CreateThread лишь передает этот параметр по эстафете той функции, с которой начинается выполнение создаваемого потока. Таким образом, данный параметр позволяет передавать функции потока какое-либо инициализирующее значение. Оно может быть или просто числовым значением, или указателем на структуру данных с дополнительной информацией.

Вполне допустимо и даже полезно создавать несколько потоков, у которых в качестве входной точки используется адрес одной и той же функции. Например, можно реализовать Web-сервер, который обрабатывает каждый клиентский запрос в отдельном потоке. При создании каждому потоку передается свое значение pvParam.

Учтите, что 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;
return(0);
}
Не исключено, что в приведенном коде FirstThread закончит свою работу до того, как SecondThread присвоит значение 5 переменной x из FirstThread. Если так и будет, SecondThread не узнает, что FirstThread больше не существует, и попытается изменить содержимое какого-то участка памяти с недействительным теперь адресом. Это неизбежно вызовет нарушение доступа: стек первого потока уничтожен по завершенииFirstThread. Что же делать? Можно объявить x статической переменной, и компилятор отведет память для хранения переменной x не в стеке, а в разделе данных приложения (application's data section). Но тогда функция станет нереентерабельной. Иначе говоря, в этом случае Вы не смогли бы создать два потока, выполняющих одну и ту же функцию, так как оба потока совместно использовали бы статическую переменную. Другое решение этой проблемы (и его более сложные варианты) базируется на методах синхронизации потоков, речь о которых пойдет в главах 8, 9 и 10.

Параметр fdwCreate
Этот параметр определяет дополнительные флаги, управляющие созданием потока. Он принимает одно из двух значений. 0 (исполнение потока начинается немедленно) или CREATE_SUSPENDED. В последнем случае система создает поток, инициализирует его и приостанавливает до последующих указаний.

Флаг CREATE_SUSPENDED позволяет программе изменить какие-либо свойства потока перед тем, как он начнет выполнять код. Правда, необходимость в этом возникает довольно редко. Одно из применений этого флага демонстрирует программа - пример JobLab из главы 5.

Параметр pdwThreadID
Последний параметр функции CreateThread - это адрес переменной типа DWORD, в которой функция возвращает идентификатор, приписанный системой новому потоку. (Идентификаторы процессов и потоков рассматривались в главе 4.)

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

koma

#9
в этой книжке написано:
параметр pvParam идентичен параметру pvParam функции потока
. а про этот параметр в функции потока в этой же книжке говорится следующее:
Система обращается к Вашей функции, передавая ей 32-битный параметр lpvThreadParm, который Вы ранее передали CreateThread.
Может кто-нить объяснит, что там должно передаваться?

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

Соре за оффтоп :(
 
04.09.2006
2 566
3
#10
если я хочу в функцию потока передать какие-то свои собственные любые данные, я могу сделать это с пом. этого параметра?
Да, при помощи pvParam можно передавать данные потокам

Может кто-нить объяснит, что там должно передаваться?
Если бы Вы нормально описывали проблему, а не выдирали фразы из книги, то можно было бы и пояснить
 
K
#11
можно передавать ЛЮБЫЕ данные, которые я захочу, а потом просто привести к нужному типу? да?