runewalsh писал(а):>64 хэндла. А если больше надо будет
Больше не нужно. Тебе не нужно вообще. Завершившиеся потоки не могут внезапно воскреснуть, так что WaitForMultipleObjects(bAll=TRUE) лишняя
С WaitForMultipleObjects я сражаюсь (нехотя) в рамках задания №1. Хотя тоже считаю, что она
runewalsh писал(а):лишняя и полностью эквивалентна for i := ... do WaitForSingleObject(Threads[i], ...).
Но преподавателя, в переписке циклит на WaitForMultipleObjects. Видимо ему понравился его пример с выделением памяти. И можно увидеть рецензию
"Ну надо было бы все таки использовать WaitForMultipleObjects.." С пометкой незачтено. И что такое это "было бы" - руководство к действию? И почему вообще незачет - пойди разберись. Такой вот он... Я дистанционник. И его рецензий можно ждать по месяцу. А семестр как у всех - полгода. А насчет надо ли больше HANDLE чем 64, то по условиям задания юзер вводит кол-во потоков в начале программы. Само условие задания достаточно громоздкое, я и так большой ломоть попросил Вас прочитать.
runewalsh писал(а):SleepEx сама вызывает один из коллбэков, которые поток попросили выполнить через QueueUserAPC, или, если их нет, ждёт следующий N мс (здесь — бесконечно).
То ли ты неправильно сформулировал, то ли я не проникся. Если следовать таким рассуждениям получается подобие такой хрени
- Код: Выделить всё
for i:=0 to n-1 do //это кусок из предыдущего кода, переделанный под вдохновением объяснения
Threads[i] := CreateThread(nil, 0, @PrintInteger, nil, 0, ThreadIDs[i].ID);//ф-ции с бесконечным SleepEx больше нет и вызывается сразу "рабочая" ф-ция.
Sem := CreateSemaphore(nil, 1, 1, nil);//ну понятно, что сразу ломится выполнять, как то можно и приостановить
repeat
for i:=0 to n-1 do
QueueUserAPC(@PrintInteger,Threads[i],ThreadIDs[i].ID);
count:=n-1;
repeat
writeln('Вызов ',count);
SleepEx(INFINITE, TRUE); //И тут попытки вызвать поток из массива, потому что SleepEx [b]сама[/b] вызывает
dec (count);
until count=-1;
until Zadacha=Zadanie;
while true — это for(;;).
Я в первый раз вижу for(;;), даже гугл не знает. Вот while true знает. Но для меня это как то дико, все равно что while NIL. Что true (истина) то? Параметр, переданный в ф-цию если только. В общем первый раз увидел - удивился, больше не буду
Дописал я пример. Прошу только отследить ход моих рассуждений, и если где то грубая ошибка - откомментить с поправкой
1. Дикое зацикливание SleepEx не нужно совсем. Поток направленный в SleepEx может только
- An I/O completion callback function is called.
- An asynchronous procedure call (APC) is queued to the thread.
- The time-out interval elapses.
Нам интересен 2-й вариант.
2. После вызова свободного (спящего) потока из SleepEx ф-цией QueueUserAPC поток выполняет ф-цию указанную в параметрах QueueUserAPC, при этом для
выполнения этой нужной нам ф-ции, можно передать ей параметры, указанные опять же в параметрах QueueUserAPC.
3. После завершения работы потока в ф-ции на которую его натравили, поток опять засыпает и становится доступным для вызова QueueUserAPC.
4. Если в SleepEx все потоки заняты, то при обращении к этой ф-ции с помощью QueueUserAPC ничего не происходит.==> Или все таки
ожидается любой первый освободившийся поток?
5. Завершение работы прихлапыванием потоков, равно как и костыли вроде
Поток с ней завершается только потому, что по выходу из тела программы FPC вызывает ExitProcess, которая неявно обрывает все потоки в духе TerminateThread.
кажутся мне дурным тоном. По хорошему надо по завершения задания (Zadanie в моем примере) натравить все эти потоки на CloseThread. С 4 моим пунктом
только определиться
6. Sleep(1) в коде нужна, видимо, для того чтобы поток, вызванный QueueUserAPC успел что то сделать, раз уж мы отталкиваемся от количества
выполненных заданий... Тоже костыль по сути. Надо подумать.
- Код: Выделить всё
USES windows;
TYPE
DBThreads= record
ID :LongWord;
Time :LongWord;
bAlertable :bool;
end;
var
i,n,count :word;
Threads :TWOHandleArray;
ThreadIDs :array of DBThreads;
Sem :HANDLE;
Step,Zadanie :LongWord;
function QueueUserAPC(pfnAPC:Pointer;hThread:HANDLE;dwData:ULONG_PTR):DWORD;
stdcall; external 'Kernel32.dll';
FUNCTION PrintInteger(Param: integer):ptrint; stdcall;
begin
WaitForSingleObject(Sem, INFINITE);
inc (Step);
Writeln('Задача: ',Step,' выполнена потоком c ID ',Param);
ReleaseSemaphore(Sem, 1, nil);
sleep (1000);
writeln(GetCurrentThreadId,' проснулся');
PrintInteger:=0;
end;
begin
write('Сколько потоков будет в пуле? ');
readln(n);
write('Сколько задач нужно выполнить? ');
readln(Zadanie);
SetLength(ThreadIDs,n);
for i:=0 to n-1 do
begin
ThreadIDs[i].Time:=INFINITE;
ThreadIDs[i].bAlertable:=true;
Threads[i] := CreateThread(nil, 0, @SleepEx, @ThreadIDs[i], 0, ThreadIDs[i].ID);
end;
Sem := CreateSemaphore(nil, 1, 1, nil);
repeat
if Zadanie-Step>=n then count:=0 else count:=n-(Zadanie-Step);
for i:=count to n-1 do
begin
QueueUserAPC(@PrintInteger,Threads[i],ThreadIDs[i].ID);
sleep(1);
end;
until Step=Zadanie;
for i:=0 to n-1 do TerminateThread(Threads[i],0);
writeln ('Задание ',Zadanie,' достигнуто за ',Step,' шагов');
writeln ('Все потоки уничтожены');
readln
end.