Форум: "Потрепаться";
Текущий архив: 2004.12.05;
Скачать: [xml.tar.bz2];
ВнизНужна критика кода-) Найти похожие ветки
← →
panov © (2004-11-11 18:05) [0]Опять я с потоками.
Вот реализация класса потока, от которого хочу наследоваться и писать специализированные классы.
Прошу покритиковать тех, у кого есть время и желание. Думаю, что слабых мест достаточно в коде.
unit uThreadClass;
interface
uses
classes,windows;
type
TThreadClass=class
private
FHandle,FThreadId: THandle;
FEvent: THandle;
FFreeOnTerminate: Boolean;
FTerminated: Boolean;
FSuspended: Boolean;
FReturnCode: Integer;
procedure FOnStartThread;
protected
procedure Execute;virtual;abstract;
public
constructor Create(const CreateSuspended: Boolean);
destructor Destroy;override;
procedure AfterConstruction; override;
procedure FOnEndThread;virtual;
procedure Suspend;
procedure Resume;
procedure WaitFor(Timeout: Cardinal=INFINITE);
procedure Terminate;
procedure Kill(ExitCode: Cardinal);
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
property Terminated: Boolean read FTerminated;
property Handle: THandle read FHandle;
property ThreadId: THandle read FThreadId;
property ReturnCode: Integer read FReturnCode write FReturnCode;
end;
implementation
procedure ThreadProcedure(const Sender: TObject); cdecl;
var
Ret: Cardinal;
begin
try
if TThreadClass(Sender).FSuspended then TThreadClass(Sender).Suspend;
TThreadClass(Sender).FOnStartThread;
TThreadClass(Sender).Execute;
TThreadClass(Sender).FOnEndThread;
Ret := 0;
except
Ret := 1;
end;
if TThreadClass(Sender).FreeOnTerminate then TThreadClass(Sender).Free;
ExitThread(Ret);
end;
{ TThreadClass }
procedure TThreadClass.AfterConstruction;
begin
inherited;
if not FSuspended then
begin
Resume;
WaitForSingleObject(FEvent,INFINITE);
end;
end;
constructor TThreadClass.Create(const CreateSuspended: Boolean);
begin
inherited Create;
FreeOnTerminate := True;
FSuspended := CreateSuspended;
FEvent := CreateEvent(nil,True,False,nil);
FHandle := CreateThread(nil,0,@ThreadProcedure,Self,CREATE_SUSPENDED,FThreadId);
AfterConstruction;
end;
destructor TThreadClass.Destroy;
begin
CloseHandle(FHandle);
CloseHandle(FEvent);
inherited;
end;
procedure TThreadClass.FOnEndThread;
begin
end;
procedure TThreadClass.FOnStartThread;
begin
if not FSuspended then SetEvent(FEvent);
end;
procedure TThreadClass.Kill(ExitCode: Cardinal);
begin
TerminateThread(FHandle,ExitCode);
Terminate;
Free;
end;
procedure TThreadClass.Resume;
begin
ResumeThread(FHandle);
end;
procedure TThreadClass.Suspend;
begin
SuspendThread(FHandle);
end;
procedure TThreadClass.Terminate;
begin
FTerminated := True;
end;
procedure TThreadClass.WaitFor(Timeout: Cardinal=INFINITE);
begin
WaitForSingleObject(FHandle,TimeOut);
end;
end.
← →
Игорь Шевченко © (2004-11-11 18:11) [1]
> procedure ThreadProcedure(const Sender: TObject); cdecl;
stdcall ?
← →
Игорь Шевченко © (2004-11-11 18:12) [2]И еще - ты б хоть объяснил, что и зачем, чтобы догадками не заниматься.
← →
Суслик © (2004-11-11 18:13) [3]1) зачем afterconstruction вызывать?
2) не уверен, что верно, но я бы closehandle делал только если реально соответсвующий handle был инициализирован.
3) зачем в aftercontruction ждать? ведь тогда повиснет вызывающий поток.
← →
DiamondShark © (2004-11-11 18:26) [4]
> CreateThread
BeginThread
← →
DiamondShark © (2004-11-11 18:28) [5]
> procedure [???] ThreadProcedure(const Sender: TObject); cdecl [?!!];
← →
DiamondShark © (2004-11-11 18:30) [6]Из конструктора вообще никогда не возвращаемся?
← →
DiamondShark © (2004-11-11 18:36) [7]А можно рассказать, зачем эти странные манипуляции с евентом?
← →
GuAV © (2004-11-11 18:41) [8]panov © (11.11.04 18:05)
TThreadClass
IMHO следует придерживаться cтандартов на названия. У Вас надо TThreadObj или как-то так, а TXxxxClass - это class of TXxxx
FOnStartThread
Особенно здесь. Это болше похоже на поле события чем на метод.
Вносит путаницу при чтении кода.
ExitThread(Ret);
end;
Зачем так по-странному ? не проще ли вернуть результат ThreadProcedure, сделав её функцией.
procedure ThreadProcedure(const Sender: TObject); stdcall;
Можно сразу написать TThreadClass заместо TObject.
А можно даже объявить stdcall - метод класса (уже без параметра).
← →
Владислав © (2004-11-11 18:57) [9]Александр, можно поинтересоваться о предназначении этого класса? Чем не устраивает TThread? И вообще, какова цель своей реализации потоковых классов?
Я к тому, что у меня есть небольшие наработки в этом направлении, быть может пригодится?
← →
panov © (2004-11-11 20:58) [10]фух... сейчас буду думать и отвечать по порядку...
← →
panov © (2004-11-11 21:28) [11]>Игорь Шевченко © (11.11.04 18:11) [1]
По-моему, стандарт и соглашение о передаче параметров в данном случае cdecl, а не stdcall.
>Игорь Шевченко © (11.11.04 18:12) [2]
Объясняю-)
Этот класс хочу использовать в пуле потоков. Он должен быть максимально простым и управляемым.
>Суслик © (11.11.04 18:13) [3]
1) зачем afterconstruction вызывать?
Вызывается для того, чтобы освободить поток(resume) в случае если он должен создаваться в неприостановленном состоянии.
2) не уверен, что верно, но я бы closehandle делал только если реально соответсвующий handle был инициализирован.
Логично. Сделаю проверку при создании потока.
3) зачем в aftercontruction ждать? ведь тогда повиснет вызывающий поток.
Для этого и сделано. Вызывающий поток должен быть уверен, что поточная функция начала работу до того, как начнутся обращения к объекту.
>DiamondShark © (11.11.04 18:26) [4]
BeginThread
В чем разница? -)
>DiamondShark © (11.11.04 18:30) [6]
Из конструктора вообще никогда не возвращаемся?
Возвращаемся после AfterConstruction в любом случае.
>DiamondShark © (11.11.04 18:36) [7]
А можно рассказать, зачем эти странные манипуляции с евентом?
Единственная цель - дождаться начала выполнения поточной функции.
>GuAV © (11.11.04 18:41) [8]
TThreadClass
IMHO следует придерживаться cтандартов на названия. У Вас надо TThreadObj или как-то так, а TXxxxClass - это class of TXxxx
FOnStartThread
Особенно здесь. Это болше похоже на поле события чем на метод.
Вносит путаницу при чтении кода.
Согласен, тут надо придумать получше навание.
ExitThread(Ret);
end;
Зачем так по-странному ? не проще ли вернуть результат ThreadProcedure, сделав её функцией.
А разве из поточной функции можно получить результат иначе как установив явно код в ExitThread?
procedure ThreadProcedure(const Sender: TObject); stdcall;
Можно сразу написать TThreadClass заместо TObject.
Возможно, но пока остановился на этом, так как(может быть) буду добавлять возможность замены поточной функции(дополнительно).
А можно даже объявить stdcall - метод класса (уже без параметра).
Пример передачи в CreateThread метода класса можно? - я с удовольствием так сделаю.
>Владислав © (11.11.04 18:57) [9]
Александр, можно поинтересоваться о предназначении этого класса? Чем не устраивает TThread? И вообще, какова цель своей реализации потоковых классов?
Я к тому, что у меня есть небольшие наработки в этом направлении, быть может пригодится?
В основной конференции была ветка про пул потоков. К сожалению, она сейчас ушла в архив, да и я не успел за это время проработать реализацию.
TThread не устраивает тем, например, что реализации его отличаются в 5-1 и 6-1 версии Delphi.
← →
GuAV © (2004-11-11 21:47) [12]panov © (11.11.04 21:28) [11]
Пример передачи в CreateThread метода класса можно?
Таки сложно но можно. Сделал на примере метода формы но думаю понятноtype
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
class procedure SomeMethod;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
ThdMethod: procedure of object = TForm1.SomeMethod
var
ThdProc: pointer absolute ThdMethod; // это и передавать
class procedure TForm1.SomeMethod;
begin
end;
AfterConstruction + F1:
AfterConstruction is called automatically after the object’s last constructor has executed. Do not call it explicitly in your applications.
panov © (11.11.04 21:28) [11]
А разве из поточной функции можно получить результат иначе как установив явно код в ExitThread?
ExitThread
The ExitThread function ends a thread.
VOID ExitThread(
DWORD dwExitCode // exit code for this thread
);
Parameters
dwExitCode
[in] Exit code for the calling thread. Use the GetExitCodeThread function to retrieve a thread"s exit code.
ThreadProc
....
Return Values
The function should return a value that indicates its success or failure.
Remarks
A process can obtain the return value of the ThreadProc of a thread it created with CreateThread by calling the GetExitCodeThread function. A process cannot obtain the return value from the ThreadProc of a thread it created with CreateRemoteThread.
← →
kaZaNoVa © (2004-11-11 21:49) [13]"прямой" вызов
BeginThread
рулит ! (имхо, чтобы без заморочек с классами)
← →
GuAV © (2004-11-11 21:51) [14]GuAV © (11.11.04 21:47) [12]
Пример с class procedure не самое лучшее, т.к. хотелось бы обычный.
Но идея та же: var TM: procedure of object; TM:=ThdMethod и потом typecase его в pointer.
← →
panov © (2004-11-11 21:56) [15]>GuAV © (11.11.04 21:47) [12]
AfterConstruction is called automatically after the object’s last constructor has executed. Do not call it explicitly in your applications.
СОгласен, завтра протестирую этот момент.
Remarks
A process can obtain the return value of the ThreadProc of a thread it created with CreateThread by calling the GetExitCodeThread function. A process cannot obtain the return value from the ThreadProc of a thread it created with CreateRemoteThread.
Тоже проверю завтра, спасибо...
За пример с методом спасибо, но тебе не кажется, что это некоторое извращение? -)
← →
GuAV © (2004-11-11 22:00) [16]Про cdecl вот
{$IFDEF MSWINDOWS}
function ThreadWrapper(Parameter: Pointer): Integer; stdcall;
{$ELSE}
function ThreadWrapper(Parameter: Pointer): Pointer; cdecl;
{$ENDIF}
За пример с методом спасибо, но тебе не кажется, что это некоторое извращение? -)
Согласен. Я просто упустил из вида момент что его надо куда то пердавать.
← →
Игорь Шевченко © (2004-11-11 22:07) [17]panov © (11.11.04 21:28) [11]
> По-моему, стандарт и соглашение о передаче параметров в
> данном случае cdecl, а не stdcall.
А что для CreateThread F1 говорит ?
А что для BeginThread RTFS System.pas говорит ?
> Этот класс хочу использовать в пуле потоков. Он должен быть
> максимально простым и управляемым.
Я не понимаю без комментариев к твоему коду, каким образом он получается "простым и управляемым".
← →
panov © (2004-11-11 22:15) [18]>GuAV © (11.11.04 22:00) [16]
>Игорь Шевченко © (11.11.04 22:07) [17]
про stdcall согласен. Не пойму, почему начал писать cdecl, ведь всегда stdcall писал.
Я не понимаю без комментариев к твоему коду, каким образом он получается "простым и управляемым".
Я завтра комментарии напишу, Игорь. Сегодня уже не успевал.
← →
GuAV © (2004-11-11 22:32) [19]panov © (11.11.04 21:28) [11]
В чем разница? -)
А действительно в чём ?
пока нашел два отличия:
Установка Coprocesor Control Word
Установка переменной IsMultiThread
← →
Verg © (2004-11-11 22:38) [20]Говрят велосипеды изобретать - способствует омоложению организЬма. :)
Это мне понравилось:
> procedure TThreadClass.Kill(ExitCode: Cardinal);
> begin
> TerminateThread(FHandle,ExitCode);
> Terminate;
> Free;
> end;
что-то зловещее, черное, стильное как семейка Адамсов :))
← →
jack128 © (2004-11-11 22:41) [21]panov © (11.11.04 18:05)
TerminateThread(FHandle,ExitCode);
Terminate;
Free;
подчеркнутое - это припарка мертвому.
> procedure FOnEndThread;virtual;
этот метод - лишний. Тот же эффект достигается перекрытием Execute. И вообще - посмотрите еще
> 3) зачем в aftercontruction ждать? ведь тогда повиснет
>вызывающий поток.
>
> Для этого и сделано. Вызывающий поток должен быть
> уверен, что поточная функция начала работу до того,
> как начнутся обращения к объекту.
покажите, где у вас ивент в сигнальное состояни переходит??? Я лично не вижу, а значит основной поток висит пока ивент не будет уничтожен, то есть будет эждать пока доп. поток не отработает..
> Вызывается для того, чтобы освободить поток(resume) в
> случае если он должен создаваться в неприостановленном
> состоянии.
а не логичнее как в VCL сделать?? просто переоватьсоответствующий флаг в CreateThread
>BeginThread
>
> В чем разница? -)
дает знать менеджер памяти о том, что приложение - многопоточное. Ну и по мелочи, может от версии дельфи что то зависит..
panov © (11.11.04 21:28) [11]
TThread не устраивает тем, например, что реализации его отличаются в 5-1 и 6-1 версии Delphi.
скопируйте любую реализацию, а не выдумывайте свою глючную ;-) (шутка, но в любой щутке .. далее по тексту..)
GuAV © (11.11.04 22:32) [19]
Установка переменной IsMultiThread
это очень много!!!!
← →
Verg © (2004-11-11 22:42) [22]Не хватает слов - "и по ветру разбросать прах твой"...
← →
jack128 © (2004-11-11 22:42) [23]jack128 © (11.11.04 22:41) [21]
этот метод - лишний. Тот же эффект достигается перекрытием Execute. И вообще - посмотрите еще подчеркнутое не читать.
← →
jack128 © (2004-11-11 22:48) [24]вообще единственное, что меня не устраивает в VCL реализации потока - это не виртуальность Terminate. Поскольку часто нужно давать потоку сигнал о том? что пора помирать не флагом FTerminated, а сигналом от ивента.
← →
Игорь Шевченко © (2004-11-11 23:00) [25]
> вообще единственное, что меня не устраивает в VCL реализации
> потока - это не виртуальность Terminate. Поскольку часто
> нужно давать потоку сигнал о том? что пора помирать не флагом
> FTerminated, а сигналом от ивента.
Напиши наследника и будет счастье
← →
Cobalt © (2004-11-11 23:06) [26]jack128 © (11.11.04 22:48) [24]
Это, по-моему, излишество :)
Признак завершения работы - пожалуй, но как признак "убития" - "Право, это черезчур" :)
← →
panov © (2004-11-11 23:24) [27]>Verg © (11.11.04 22:38) [20]
что-то зловещее, черное, стильное как семейка Адамсов :))
Ну вообще-то убить поток еще не значит освободить объект-оболочку-)
>GuAV © (11.11.04 22:32) [19]
IsMultiThread упустил из вида, действительно.
>jack128 © (11.11.04 22:41) [21]
TerminateThread(FHandle,ExitCode);
Terminate;
Free;
подчеркнутое - это припарка мертвому.
Здесь устанавливается FTerminated для того, чтобы для любого внешнего обращения (Terminated) до освобождения объекта было возвращено Terminated=True
Покажите, где у вас ивент в сигнальное состояни переходит??? Я лично не вижу, а значит основной поток висит пока ивент не будет уничтожен, то есть будет эждать пока доп. поток не отработает..procedure TThreadClass.FOnStartThread;
begin
if not FSuspended then SetEvent(FEvent);
end;
Про IsMultiThread уже выше написал-)
← →
Игорь Шевченко © (2004-11-11 23:30) [28]panov © (11.11.04 23:24) [27]
> > procedure TThreadClass.Kill(ExitCode: Cardinal);
> > begin
> > TerminateThread(FHandle,ExitCode);
> > Terminate;
> > Free;
> > end;
В контексте какого потока выполняется этот код ?
А за Free внутри метода нужно, IMHO, руки отрывать сразу.
← →
panov © (2004-11-11 23:34) [29]>Игорь Шевченко © (11.11.04 23:30) [28]
Free вызывается в контексте любого потока.
А за Free внутри метода нужно, IMHO, руки отрывать сразу.
А чем здесь плох вызов Free с учетом того, что далее отработает только Destroy?
← →
GuAV © (2004-11-11 23:36) [30]Игорь Шевченко © (11.11.04 23:30) [28]
А за Free внутри метода нужно, IMHO, руки отрывать сразу.
Аргументируйте.
← →
Verg © (2004-11-11 23:38) [31]
> [28] Игорь Шевченко © (11.11.04 23:30)
> В контексте какого потока выполняется этот код ?
"Может выполняться".
Если в контексте самого FHandle, то чудные чудеса можно получить. В том числе и мемликов и всяких прочих "чудес"...
А вообще потоки "убивать" нельзя. Это должно быть железным правилом. Поток должен быть достаточно послушен, чтобы прекратить за приемлимое время свое существование самостоятельно по команде "менеджера".
← →
Игорь Шевченко © (2004-11-11 23:40) [32]GuAV © (11.11.04 23:36) [30]
Аргументирую: у меня есть переменная, указывающая на некий объект. Я вызываю некий метод, после которого переменная становится недействительной. Достаточно ?
← →
GuAV © (2004-11-11 23:43) [33]Игорь Шевченко © (11.11.04 23:40) [32]
Достаточно ?
А метод Free это ведь тоже некий метод ?
Хотя конечно можно было бы сделать метод Kill desturctorом.
← →
Игорь Шевченко © (2004-11-11 23:53) [34]GuAV © (11.11.04 23:43) [33]
Вот метод Free является одним таким исключением, причем для любого объекта.
Любой другой метод, обладающий подобным побочным эффектом - кандидад в Recycle Bin.
← →
GuAV © (2004-11-12 00:07) [35]Игорь Шевченко © (11.11.04 23:53) [34]
Вот метод Free является одним таким исключением
TThread.Resume и TCustomForm.Release могут также обладать таким эффектом.
← →
Игорь Шевченко © (2004-11-12 00:16) [36]GuAV © (12.11.04 00:07) [35]
> TCustomForm.Release
Доказать можешь ? Что после вызова Release указатель на форму в следующей команде станет недействительным.
> TThread.Resume
Да, при FreeOnTerminate. C этим согласен. Более того, даже TThread.Create. Кстати, при FreeOnTerminate освобождение объекта происходит в контексте порожденного потока, следовательно, указатель на него вряд ли стоит хранить.
← →
GuAV © (2004-11-12 00:21) [37]Игорь Шевченко © (12.11.04 0:16) [36]
в следующей команде
В следующей - нет. Но после Application.Processmessages - запросто.
← →
jack128 © (2004-11-12 00:26) [38]Игорь Шевченко © (12.11.04 0:16) [36]
> Доказать можешь ? Что после вызова Release указатель
> на форму в следующей команде станет недействительным.
ну очевидно не на следующей, но какая разница? Если это действие документировано. Да с Release локальная переменная никогда не станет инвалидной, но, например, поле объекта - станет.
Вообще - имеет ли объект право уничтожать себя сам?
← →
GuAV © (2004-11-12 00:26) [39]Игорь Шевченко © (12.11.04 0:16) [36]
Более того, даже TThread.Create.
А вот это уже никак :-) TThread.Create не устанавливает FreeOnTerminate => он остаётся False. Что касается наследников, то они не изменяют TThread.Create а просто скрывают его, т.к. он не виртуальный
← →
jack128 © (2004-11-12 00:28) [40]GuAV © (12.11.04 0:21) [37]
Application.Processmessages
хм, да. Но это все таки тоже исключение. С этом методом надо очень осторожно оброщаться.
← →
GuAV © (2004-11-12 00:28) [41]jack128 © (12.11.04 0:26) [38]
Да с Release локальная переменная никогда не станет инвалидной
И при Application.Processmessages ?
jack128 © (12.11.04 0:26) [38]
Вообще - имеет ли объект право уничтожать себя сам?
Некоторые объекты имеют => в общем случае IMHO да.
← →
Игорь Шевченко © (2004-11-12 00:36) [42]GuAV © (12.11.04 00:26) [39]
А ведь точно, спутал с наследниками. Которые устанавливают у себя FreeOnTerminate. Спасибо :)
GuAV © (12.11.04 00:21) [37]
До кучи вспомним еще TFrom.Close с Action равной caFree.
Но у меня все-таки вопрос к автору - в контекте какого потока выполняется его метод Kill ? Если в контексте созданного потока, то код абсолютно неработоспособный. Если в контексте создавающего потока, то код потенциально опасный из-за Free внутри метода.
← →
Verg © (2004-11-12 01:02) [43]
> Но у меня все-таки вопрос к автору - в контекте какого потока
> выполняется его метод Kill
Глаавно, что это противоречие неустранимо.
Т.е Kill должен быть гарантированно вызван не в контексте потока, которым управляет этот объект. Иначе - либо потеря самого объекта (мемлик), либо потеря объекта ядра из-за незакрытия Handle этого потока.
Второе "либо" относится к такому "эндшпилю":
procedure TThreadClass.Kill(ExitCode: Cardinal);
var H : THandle;
begin
H := FHandle;
Free;
// При учете, что CloseHande(FHandle) перенесли из деструктора в ThreadProcedure перед ExitThread;
TerminateThread( H, ExitCode );
// Напрашивающийся тут мат в один ход уже никому не нужен :))
CloseHandle(H); // - Unreachable Code, хотя кто-то должен же закрыть этот хендель
end;
Да, и кому может понадобиться вызов Terminate в оригинальном коде Kill ? :)))
← →
GuAV © (2004-11-12 01:20) [44]Verg © (12.11.04 1:02) [43]
Целый поток был грубо прибит, что настолько плохо что не думаю что один незакрытый хэндл сделает большую разницу :)
← →
Игорь Шевченко © (2004-11-12 01:24) [45]GuAV © (12.11.04 01:20) [44]
Считается ли прибитым поток, если у него остался незакрытый Handle ? :)
← →
GuAV © (2004-11-12 01:40) [46]Игорь Шевченко © (12.11.04 1:24) [45]
Нет, он не убитый, это подранок :)
Короче, таки надо так:if GetCurrentThreadId = FThreadId the
ExitThread(...)
else
TerminateThread(...)
...
← →
GuAV © (2004-11-12 01:47) [47]2 Verg ©
Кстати, а как же тогда реализован TThread.FreeOnTerminate
← →
panov © (2004-11-12 02:00) [48]Хочу все-же пояснить про Kill.
Объект создает поток.
В этом объекте есть метод Kill.
Метод Kill:
1. Убивает поток (TerminateThread), но не объект!
2. Устанавливает флаг завершения для того, чтобы любой наследник(не он сам) мог видеть, что поточная функция завершена(в процессе завершения) и нет смысла в этом смысле передавать/принимать от объекта какие-либо данные.
3. Независимо от значения FreeOnTerminate вызывает деструктор Destroy, который закрывает 2 созданных дескриптора в объекте.
Т.е. метод Kill позволяет корректно уничтожить сам объект и освободить все ресурсы, за исключением созданных в поточной функции.
Метод сделан для простого уничтожения зависшего потока с минимальными потерями ресурсов.
← →
Игорь Шевченко © (2004-11-12 10:18) [49]panov © (12.11.04 02:00) [48]
[43] читал ?
В контексте какого потока будет выполняться метод Kill ?
← →
Владислав © (2004-11-12 10:21) [50]Таки на вопрос Игоря не ответили :)
← →
Cobalt © (2004-11-12 10:41) [51]2 Игорь Шевченко © (12.11.04 10:18) [49]
Т.к. Александр позиционирует этот метод, как
для простого уничтожения зависшего потока
,то не из самого потока :)
← →
Игорь Шевченко © (2004-11-12 10:46) [52]Cobalt © (12.11.04 10:41) [51]
Тогда все мои возражения остаются в силе. Код абсолютно нерабочий.
← →
Digitman © (2004-11-12 11:05) [53]
> panov © (11.11.04 21:28) [11]
> Этот класс хочу использовать в пуле потоков. Он должен быть
> максимально простым и управляемым
Если уж на то пошло (пул здесь фигурирует), то лучше бы ты сосредоточил мозговые усилия на реализации трэд-класса, "заточенного" к использованию именно какими-то пул-менеджерами.. возьми за ориентир, скажем, все тот же TServerClientThread .. просто разработай свои TCustomThreadPoolMgr, TCustomThreadPool, TCustomPooledThread и сделай грамотную и надежную "связку" между
ними - это, пожалуй, будет гораздо полезней, нежели "курочить" TThread на свой манер
а по поводу простоты и управляемости - не вижу сколь-либо ощутимого повода для нареканий в отношении этого в станд.реализации TThread
imho
← →
panov © (2004-11-12 12:45) [54]>Игорь Шевченко © (12.11.04 10:46) [52]
Повторю: Kill сделан не для вызова из поточной функции.
Т.е. из любого другого потока, кроме созданного CreateThread в конструкторе.
Тогда все мои возражения остаются в силе. Код абсолютно нерабочий.
Можно пояснить, какой именно момент нерабочий и чем это вызвано?
← →
Игорь Шевченко © (2004-11-12 12:56) [55]panov © (12.11.04 12:45) [54]
Можно написать комментарии все-таки ?
← →
PVOzerski © (2004-11-12 13:00) [56]2[54]:
Тогда нужно проверять потоковую принадлежность кода, вызывающего Kill и "в случае чего" генерить исключение. IMHO, конечно. Но проверить-то можно. Exception выглядит убедительнее, чем рекомендации копаться в документации :^) Что-нибудь вроде
procedure TThreadClass.Kill(ExitCode: Cardinal);
begin
if GetCurrentThreadId<>FThreadId then
raise что нибудь;
← →
panov © (2004-11-12 13:06) [57]>Игорь Шевченко © (12.11.04 12:56) [55]
Сейчас исправлю с учетом замечаний и снова выложу.
← →
REA (2004-11-12 13:09) [58]>Этот класс хочу использовать в пуле потоков
А используется Thread Pooling? (QueueUserWorkItem, BindIoCompletionCallback там всякие)
← →
panov © (2004-11-12 13:10) [59]>REA (12.11.04 13:09) [58]
А в W95-WinNT?
← →
panov © (2004-11-12 14:16) [60]
unit uThreadClass;
interface
uses
classes,windows;
type
TSimpleThreadObj=class
private
FHandle,FThreadId: THandle; //Handle & ThreadId создаваемого потока
FEvent: THandle; //Event для ожидания завершения инициализации
FFreeOnTerminate: Boolean; //Флаг автоматического разрушения объекта
// после окончания выполнения поточной функции
FTerminated: Boolean; //Флаг состояния завершения потока
FSuspended: Boolean; //Флаг состояния Thread Suspended
FReturnCode: Integer; //Код возврата поточной функции
procedure OnStartThread; //Установка состояния "Поток создан"
protected
procedure Execute;virtual;abstract;
public
constructor Create(const CreateSuspended: Boolean);
destructor Destroy;override;
procedure AfterConstruction; override; //Перекрытие AfterConstruction
procedure OnEndThread;virtual;abstract; //Событие, возникнет перед окончанием
// поточной функции
procedure Suspend; //Приостановить поток
procedure Resume; //Разбудить поток
procedure WaitFor(Timeout: Cardinal=INFINITE); //Ждать завершения поточной
// функции
procedure Terminate; //Установить флаг состояния завершения потока
procedure Kill(ExitCode: Cardinal); //Завершить выполнение поточной функции
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
property Terminated: Boolean read FTerminated;
property Handle: THandle read FHandle;
property ThreadId: THandle read FThreadId;
property ReturnCode: Integer read FReturnCode write FReturnCode;
end;
implementation
//function ThreadProcedure(const Sender: TObject): Cardinal; stdcall;
//
// Поточная функция
// Параметр Sender - ссылка на объект класса TSimpleThreadObj,
// в котором создан поток
//
function ThreadProcedure(const Sender: TObject): Cardinal; stdcall;
begin
Result := 0; //Устанавливаем код возврата в 0
try
TSimpleThreadObj(Sender).OnStartThread; //Поточная функция начала работать,
TSimpleThreadObj(Sender).Execute; //Собственно выполнение пользовательского кода
TSimpleThreadObj(Sender).OnEndThread; //Поточная функция законлила выполнение кода
except
Result := INVALID_HANDLE_VALUE;
end;
// Если установлен флаг автоматического уничтожения объекта - вызыыаем Free
if TSimpleThreadObj(Sender).FreeOnTerminate then TSimpleThreadObj(Sender).Free;
end;
{ TSimpleThreadObj }
//procedure TSimpleThreadObj.AfterConstruction;
//
// метод вызывается сразу после завершения конструктора
// назначение: при необходимости дождаться, пока начнет работу поточная функция.
//
procedure TSimpleThreadObj.AfterConstruction;
begin
inherited;
//если поток создается не в приостановленном состоянии, ждем,
// пока будет установлен FEvent
if not FSuspended then
begin
Resume;
WaitForSingleObject(FEvent,INFINITE);
end;
end;
//constructor TSimpleThreadObj.Create(const CreateSuspended: Boolean);
//
// Конструктор
// Параметры;
// CreateSuspended: Boolean - True - поток создается в приостановленном состоянии
//
constructor TSimpleThreadObj.Create(const CreateSuspended: Boolean);
begin
inherited Create;
FreeOnTerminate := True; //по умолчанию объект
// уничтожается автоматически
FSuspended := CreateSuspended;
FEvent := CreateEvent(nil,True,False,nil);
FHandle := CreateThread(nil,0,@ThreadProcedure,Self,CREATE_SUSPENDED,FThreadId);
end;
//destructor TSimpleThreadObj.Destroy;
//
// Деструктор
//
destructor TSimpleThreadObj.Destroy;
begin
CloseHandle(FHandle);
CloseHandle(FEvent);
inherited;
end;
//procedure TSimpleThreadObj.OnStartThread;
//
// метод вызывается после старта поточной функции
//
procedure TSimpleThreadObj.OnStartThread;
begin
// Устанавливаем Event в Sinaled - поточная функция начала выполняться
if not FSuspended then SetEvent(FEvent);
end;
//procedure TSimpleThreadObj.Kill(ExitCode: Cardinal);
//
// метод вызывается для "жесткого" уничтожения потока
// TerminateThread(например, для уничтожения "зависшего" потока)
//
procedure TSimpleThreadObj.Kill(ExitCode: Cardinal);
begin
if GetCurrentThreadId=FThreadId then Exit;
Terminate; //Устанавливаем состояние завершения
TerminateThread(FHandle,ExitCode); //Уничтожаем поток
if FreeOnTerminate then Free;
end;
//procedure TSimpleThreadObj.Resume;
//
// "Пробуждение" потока
//
procedure TSimpleThreadObj.Resume;
begin
ResumeThread(FHandle);
FSuspended := False;
end;
//procedure TSimpleThreadObj.Suspend;
//
// "Усыпление" потока
//
procedure TSimpleThreadObj.Suspend;
begin
SuspendThread(FHandle);
FSuspended := True;
end;
//procedure TSimpleThreadObj.Terminate;
//
// устанавливаем флаг состояния завершения
//
procedure TSimpleThreadObj.Terminate;
begin
FTerminated := True;
end;
//procedure TSimpleThreadObj.WaitFor(Timeout: Cardinal=INFINITE);
//
// Ожидание завершения потока
//
procedure TSimpleThreadObj.WaitFor(Timeout: Cardinal=INFINITE);
begin
WaitForSingleObject(FHandle,TimeOut);
end;
end.
← →
panov © (2004-11-12 14:17) [61]>Digitman © (12.11.04 11:05) [53]
На основе этого класса я и хочу написать менеджер пула потоков, и реализовать сам пул.
← →
Игорь Шевченко © (2004-11-12 14:23) [62]
> procedure OnEndThread;virtual;abstract; //Событие, возникнет
> перед окончанием
> // поточной функции
Его надо ОБЯЗАТЕЛЬНО реализовать в потомках ?
← →
GuAV © (2004-11-12 14:30) [63]panov © (12.11.04 14:16) [60]
procedure OnEndThread;virtual;abstract; //Событие, возникнет перед окончанием
// поточной функции
По имени и по коментарию событие, а по коду virtual;abstract; - абстрактный метод. Не хорошо. Вносит путаницу.
← →
panov © (2004-11-12 14:50) [64]>Игорь Шевченко © (12.11.04 14:23) [62]
Нет, не обязательно-)
заменил на
procedure OnEndThread;virtual;
← →
Игорь Шевченко © (2004-11-12 14:52) [65]panov © (12.11.04 14:50) [64]
Пост [63]. Лично у меня объявление чего-либо, начинающегося с On виртуальным вызывает подозрительность.
Call me paranoid but finding "/*" inside this comment makes me suspicious
(с)
← →
panov © (2004-11-12 14:55) [66]>Игорь Шевченко © (12.11.04 14:52) [65]
Пост [63]. Лично у меня объявление чего-либо, начинающегося с On виртуальным вызывает подозрительность.
Почему?-)
Так как тогда лучше назвать?-)
← →
Игорь Шевченко © (2004-11-12 15:01) [67]
> Так как тогда лучше назвать?-)
Без On (кстати, к OnStartThread это тоже относится)
← →
panov © (2004-11-12 15:11) [68]>Игорь Шевченко © (12.11.04 15:01) [67]
Переобозвал.
procedure StartThread;
procedure EndThread;
← →
GuAV © (2004-11-12 21:13) [69]panov © (12.11.04 13:06) [57]
Сейчас исправлю с учетом замечаний и снова выложу.
panov © (11.11.04 23:24) [27]
IsMultiThread упустил из вида
← →
panov © (2004-11-12 21:33) [70]>GuAV © (12.11.04 21:13) [69]
IsMultiThread упустил из вида
Да, я уже заметил, спасибо.
Теперь уже реализую класс-менджер потоков и собственно класс потока, который должен выполняться в пуле.
← →
panov © (2004-11-16 12:14) [71]Наконец-то плюнул на собственную реализацию поточного класса(хотя и зря, может быть), реализовал первую рабочую версию пула потоков.
Конечно, не претендую на совершенство и маскимально оптимизированное решение, но эта версия хотя бы работает-)
(Версия нумбер 3).
Реализовано 4 класса:
TThreadPool - менеджер потоков.
TThreadsThr - собственно поток из пула.
TPushPop - потокобезопасная работа с очередью заданий на выполнение(двусвязный список).
TSafeArray - потокобезопасная работа с массивом указателей(динамический массив потоков).
Версия рабочая, но некоторые примочки еще можно добавлять.
Если кого-то интересует, то исходники могу выложить.
Вот только не знаю, сюда или нет. Код в 3-х модулях, 500 строк.
← →
panov © (2004-11-16 12:40) [72]Основной модуль.
unit uThreadPool;
interface
uses
Classes, Windows, SysUtils, SyncObjs,
uSafeArray, uPushPop;
type
TProcThr=procedure(parm: Pointer); //клиентская процедура для выполнения в пуле потоков
// Служебная структура для передачи параметров
PJob=^TJob;
TJob=record
Proc: TProcThr; //клиентская процедура
Parm: Pointer; //параметры процедуры
end;
TThreadsPool=class;
// TThreadsThr=class(TThread)
// Поток для выполнения в пуле
TThreadsThr=class(TThread)
private
FThreadsPool: TThreadsPool; //Ссылка на менеджер потоков
FEvent: TSimpleEvent; //Event для ожидания окончания инициализации
FEventExec: TSimpleEvent; //Event для управления циклом в Execute
FProc: TProcThr; //Клиентская процедура
FParm: Pointer; //Параметры клиентской процедуры
FIsFree: Boolean; //Флаг "свободности" потока
constructor Create; //Конструктор(не вызывается напрямую)
protected
procedure Execute; override;
public
destructor Destroy;override;
class function CreateThr(Pool: TThreadsPool):TThreadsThr; //Функция для создания потока
procedure SetJob(Proc: TProcThr;Parm: Pointer); //Установка параметров потока
property isFree: Boolean read FIsFree write FIsFree; //Флаг "свободности" потока
end;
// TThreadsThr=class(TThread)
// Пул потоков
TThreadsPool=class(TThread)
private
FJobs: TPushPop; //Очередь заданий на выполнение в пуле
FPool: TSafeArray; //Пул потоков
FMaxThreads: Integer; //Макс. количество потоков в пуле
FEvent: TSimpleEvent; //Event для ожидания окончания инициализации пула
FEventAddJob: TSimpleEvent; //Event для Управления циклом в Execute
constructor Create(const MaxThreads: Integer); //Конструктор(не вызывается напрямую)
function GetFreeThr: Integer; //Поиск свободного потока в пуле
procedure EndJob(Sender: TThreadsThr); //обработка завершения выполнения задания
procedure EndThread(Sender: TThreadsThr); //Обработка завершения потока в пуле
function GetCountJobs: Integer; //Количество заданий в очереди
function GetCountThreads: Integer; //Количество потоков в пуле
function GetCountFreeThr: Integer; //Количество свобожных потоков в пуле
protected
procedure Execute; override;
public
destructor Destroy;override;
class function CreatePool(const MaxThreads: Integer):TThreadsPool; //Функция для создания пула
procedure AddJob(Proc: TProcThr;Parm: Pointer); //Добавление задания в очередь
property CountJobs: Integer read GetCountJobs; //Количество заданий в очереди
property CountThreads: Integer read GetCountThreads; //Количество потоков в пуле
property CountFreeThr: Integer read GetCountFreeThr; //Количество свобожных потоков в пуле
end;
implementation
{ TThreadsPool }
constructor TThreadsPool.Create(const MaxThreads: Integer);
begin
inherited Create(True);
FreeOnTerminate := True;
FMaxThreads := MaxThreads;
FEvent := TSimpleEvent.Create;
FEventAddJob := TSimpleEvent.Create;
FEvent.ResetEvent;
FJobs := TPushPop.Create; //Очередь заданий
FPool := TSafeArray.Create; //Массив(пул) потоков
end;
class function TThreadsPool.CreatePool(const MaxThreads: Integer):TThreadsPool;
begin
Result := TThreadsPool.Create(MaxThreads);
Result.Resume;
case Result.FEvent.WaitFor(INFINITE) of //ждем окончания инициализации
wrSignaled: Exit;
wrTimeOut,
wrAbandoned,
wrError:
begin
Result.Terminate;
Result := nil;
Exit;
end;
end;
end;
destructor TThreadsPool.Destroy;
var
i: Integer;
begin
if Assigned(FEvent) then FEvent.Free;
if Assigned(FEventAddJob) then FEventAddJob.Free;
//Очистка очереди заданий
while FJobs.Count<>0 do Dispose(PJob(FJobs.Pop));
FJobs.Free;
//Освобождение потоков в пуле
for i := 0 to FPool.Count-1 do TThreadsThr(FPool[i]).Terminate;
FPool.Free;
inherited;
end;
procedure TThreadsPool.Execute;
var
RC: TWaitResult;
Thr: TThreadsThr;
NumThr: Integer;
Job: PJob;
begin
//Странное какое-то состояние. - Terminate
if FMaxThreads=0 then
begin
Terminate;
Exit;
end;
FEvent.SetEvent; //Инициализация закончена, сообщаем.
while not Terminated do
begin
//Ждем наступления события "Есть задание в очереди"
RC := FEventAddJob.WaitFor(100);
if Terminated then break;
case RC of
wrSignaled : //Появилось задание
begin
FEventAddJob.ResetEvent; //Сбрасываем Event
NumThr := GetFreeThr; //Получить номер свободного потока
//Проверяем наличие заданий в очереди, пока есть свободные потоки
// и задания
while (FJobs.Count<>0) and (NumThr<>-1) do
begin
if NumThr=-1 then Continue; //Нет свободных потоков
Thr := FPool.Items[NumThr];
Thr.isFree := False; //Флаг "Поток занят"
Job := FJobs.Pop; //Извелекаем задание из очереди
Thr.SetJob(Job^.Proc,Job.Parm); //Устанавливаем параметры для выполнения
Dispose(Job); //Освободить память
Thr.FEventExec.SetEvent; //Известить поток о готовности к выполнению
NumThr := GetFreeThr; //Получить номер свободного потока
end;
Continue;
end;
wrTimeout:
begin
FEventAddJob.SetEvent; //Проверить очередь заданий
end;
else
begin
Terminate; //Ошибка(??) завершаем работу
end;
end;
end;
end;
procedure TThreadsPool.AddJob(Proc: TProcThr; Parm: Pointer);
var
Job: PJob;
begin
New(Job);
Job^.Proc := @Proc;
Job^.Parm := Parm;
FJobs.Push(Job); //Поместить задание в очередь
FEventAddJob.SetEvent; //Обработать очередь заданий(в Execute)
end;
function TThreadsPool.GetFreeThr: Integer;
var
i: Integer;
begin
Result := -1;
for i := 0 to FPool.Count-1 do
begin
//Проверка флага "Поток свободен"
if TThreadsThr(FPool[i]).isFree then
begin
Result := i;
break;
end;
end;
if Result=-1 then //Нет свободных потоков
begin
if FPool.Count<FMaxThreads then //Количество потоков меньше максимального значения?
begin
Result := FPool.Add(TThreadsThr.CreateThr(Self)); //Создаем новый поток в пуле
end;
end;
end;
procedure TThreadsPool.EndJob(Sender: TThreadsThr);
begin
Sender.IsFree := True; //Устанавливаем флаг "Поток свободен"
FEventAddJob.SetEvent; //Обработать очередь заданий(в Execute)
end;
procedure TThreadsPool.EndThread(Sender: TThreadsThr);
begin
FPool.Delete(FPool.IndexOf(Sender)); //Удалить поток из пула
end;
← →
panov © (2004-11-16 12:41) [73]
function TThreadsPool.GetCountJobs: Integer;
begin
Result := FJobs.Count; //Количество заданий в очереди
end;
function TThreadsPool.GetCountThreads: Integer;
begin
Result := FPool.Count; //Количество потоков в пуле
end;
function TThreadsPool.GetCountFreeThr: Integer;
var
i: Integer;
begin
//Количество свободных потоков в пуле
Result := 0;
for i := 0 to FPool.Count-1 do
begin
if TThreadsThr(FPool[i]).IsFree then Inc(Result);
end;
end;
{ TThreadsThr }
constructor TThreadsThr.Create;
begin
inherited Create(True);
FreeOnTerminate := True;
FEvent := TSimpleEvent.Create;
FEventExec := TSimpleEvent.Create;
IsFree := False;
FEvent.ResetEvent;
end;
class function TThreadsThr.CreateThr(Pool: TThreadsPool): TThreadsThr;
begin
Result := TThreadsThr.Create;
Result.FThreadsPool := Pool;
Result.Resume;
case Result.FEvent.WaitFor(INFINITE) of
wrSignaled: Exit;
wrTimeOut,
wrAbandoned,
wrError:
begin
Result.Terminate;
Result := nil;
Exit;
end;
end;
end;
destructor TThreadsThr.Destroy;
begin
if Assigned(FEvent) then FEvent.Free;
if Assigned(FEventExec) then FEventExec.Free;
inherited;
end;
procedure TThreadsThr.SetJob(Proc: TProcThr;Parm: Pointer);
begin
FProc := Proc;
FParm := Parm;
end;
procedure TThreadsThr.Execute;
var
RC: TWaitResult;
begin
FEvent.SetEvent;
isFree := True;
while not Terminated do
begin
RC := FEventExec.WaitFor(100);
FEventExec.ResetEvent;
case RC of
wrSignaled:
begin
try
isFree := False;
FProc(FParm); //Выполняем клиентскую процедуру
except
end;
FThreadsPool.EndJob(Self); //Извещаем менеджер потоков об окончании
end;
wrTimeout:
begin
end;
else
begin
FThreadsPool.EndThread(Self); //Извещаем менеджер потоков о завершении потока
Terminate;
end;
end;
end;
end;
end.
← →
panov © (2004-11-16 12:41) [74]
{
Защищенный связный список для работы с очередью
в многопоточном режиме(первый вошел-первый вышел)
}
unit uPushPop;
interface
uses
windows;
type
PItemList=^TItemList;
TItemList=record
Prev,Next: PItemList;
Data: Pointer;
end;
TPushPop=class
private
FCS: RTL_CRITICAL_SECTION;
FRootItem: PItemList;
FLastItem: PItemList;
FItemsCount: Integer;
procedure Lock;
procedure Unlock;
procedure DeleteItem0;
function GetCount: Integer;
public
constructor Create;
destructor Destroy;override;
procedure Push(const aObject: Pointer);
function Pop: Pointer;
function Peek: Pointer;
property Count: Integer read GetCount;
end;
implementation
{ TPushPop }
constructor TPushPop.Create;
begin
New(FRootItem);
New(FLastItem);
FRootItem.Prev := nil;
FRootItem.Next := FLastItem;
FRootItem.Data := nil;
FLastItem.Prev := FRootItem;
FLastItem.Data := nil;
FLastItem.Next := nil;
FItemsCount := 0;
InitializeCriticalSection(FCS);
end;
destructor TPushPop.Destroy;
begin
Lock;
while FRootItem.Next<>FLastItem do DeleteItem0;
Dispose(FRootItem);
Dispose(FLastItem);
Unlock;
end;
procedure TPushPop.Lock;
begin
EnterCriticalSection(FCS);
end;
procedure TPushPop.Unlock;
begin
LeaveCriticalSection(FCS);
end;
procedure TPushPop.DeleteItem0;
var
p: PItemList;
begin
if FItemsCount=0 then Exit;
p := FRootItem.Next;
p.Prev.Next := p.Next;
p.Next.Prev := p.Prev;
Dispose(p);
Dec(FItemsCount);
end;
function TPushPop.Peek: Pointer;
begin
Lock;
try
Result := FRootItem.Next.Data;
finally
Unlock;
end;
end;
function TPushPop.Pop: Pointer;
begin
Lock;
try
Result := FRootItem.Next.Data;
DeleteItem0;
finally
Unlock;
end;
end;
procedure TPushPop.Push(const aObject: Pointer);
var
p: PItemList;
begin
Lock;
try
New(p);
p.Data := aObject;
p.Prev := FLastItem.Prev;
p.Next := FLastItem;
FLastItem.Prev.Next := p;
FLastItem.Prev := p;
Inc(FItemsCount);
finally
Unlock;
end;
end;
function TPushPop.GetCount: Integer;
begin
Lock;
try
Result := FItemsCount;
finally
Unlock;
end;
end;
end.
← →
panov © (2004-11-16 12:42) [75]
{
TSafeArray=class
Класс для работы с дин. массивом
в многопоточном приложении
}
unit uSafeArray;
interface
uses
windows;
type
TSafeArray=class
private
FCS: RTL_CRITICAL_SECTION; //Крит. секция для защиты массива
FArray: array of Pointer;
procedure Lock; //Вход в крит. секцию
procedure Unlock; //Выход из крит. секции
function GetItem(const Index: Integer): Pointer;
procedure SetItem(const Index: Integer; const Value: Pointer);
public
constructor Create;
destructor Destroy; override;
function Add(const Item: Pointer): Integer;
function IndexOf(const Item: Pointer): Integer;
function Count: Integer;
procedure Delete(const Index: Integer);
procedure Clear;
property Items[const Index: Integer]: Pointer read GetItem write SetItem;default;
end;
implementation
{ TSafeArray }
function TSafeArray.Add(const Item: Pointer): Integer;
begin
Lock;
Result := Length(FArray);
SetLength(FArray,Result+1);
FArray[Result] := Item;
Unlock;
end;
procedure TSafeArray.Clear;
begin
Lock;
SetLength(FArray,0);
Unlock;
end;
function TSafeArray.Count: Integer;
begin
Result := Length(FArray);
end;
constructor TSafeArray.Create;
begin
InitializeCriticalSection(FCS);
end;
procedure TSafeArray.Delete(const Index: Integer);
var
i: Integer;
Len: Integer;
begin
Len := Length(FArray);
if Index<0 then Exit;
if Index>Len-1 then Exit;
Lock;
for i := Index+1 to Len-1 do
begin
FArray[i-1] := FArray[i];
end;
SetLength(FArray,Len-1);
Unlock;
end;
destructor TSafeArray.Destroy;
begin
DeleteCriticalSection(FCS);
SetLength(FArray,0);
inherited;
end;
function TSafeArray.GetItem(const Index: Integer): Pointer;
begin
Lock;
Result := FArray[Index];
Unlock;
end;
function TSafeArray.IndexOf(const Item: Pointer): Integer;
var
i: Integer;
begin
Result := -1;
Lock;
for i := 0 to Length(FArray)-1 do
begin
if Item=FArray[i] then
begin
Result := i;
break;
end;
end;
Unlock;
end;
procedure TSafeArray.Lock;
begin
EnterCriticalSection(FCS);
end;
procedure TSafeArray.SetItem(const Index: Integer; const Value: Pointer);
begin
if (Index<0) or (Index>Length(FArray)-1) then Exit;
Lock;
FArray[Index] := Value;
Unlock;
end;
procedure TSafeArray.Unlock;
begin
LeaveCriticalSection(FCS);
end;
end.
← →
panov © (2004-11-16 12:46) [76]Пример использования:
type
PRec=^Rec;
Rec=record
n: Integer;
Number: Integer;
end;
var
Pool: TThreadsPool;
procedure Exec(parm: Pointer);
var
R: PRec;
begin
R := parm;
Sleep(R.n);
//здесь нужна синхронизация
fMain.lb.Items.Add(IntToStr(R.Number)+":"+IntToStr(R.n));
Dispose(R);
end;
//Создание пула
procedure TfMain.Button1Click(Sender: TObject);
begin
Pool := TThreadsPool.CreatePool(100);
end;
//Создание заданий на выполнение
procedure TfMain.Button2Click(Sender: TObject);
var
r: PRec;
i: Integer;
begin
Randomize;
for i := 0 to 1000 do
begin
New(r);
r.Number := i;
r.n := Random(1000)*10+100;
Pool.AddJob(@Exec,r);
end;
end;
← →
Digitman © (2004-11-16 14:11) [77]
> panov
а стоило ли городить огород с собственной реализацией thread-safe-очереди (TPushPop) ?
есть же TThreadList, можно же было или унаследоваться от него или включить его как член класса TPushPop, дабы максимально использовать уже готовую реализованную в TThreadList функциональность .. там тебе и крит.секция готовая и список с произвольным доступом .. хошь используй его как очередь, хошь как стек ...
опять же и TSafeArray это касаемо ...
не вижу резона вводить thread-safe-дин.массив вместо стандартного TThreadList .. к тому же бесконечные реаллокации памяти при операциях с дин.массивом ощутимо повлияют на общую производительность алгоритма..
и вот еще что бросается в глаза - ты делаешь Lock, а нижеследующий Unlock у тебя небезусловен, что рано или поздно приведет к коллизиям из-за потенциально возможных исключениях между Lock и Unlock
← →
panov © (2004-11-16 14:39) [78]
> и вот еще что бросается в глаза - ты делаешь Lock, а
> нижеследующий Unlock у тебя небезусловен, что рано или
> поздно приведет к коллизиям из-за потенциально
> возможных исключениях между Lock и Unlock
Я не заметил, где такое. Если есть, то это ошибка.
Вроде бы как везде такой формат:Lock;
try
finally
Unlock;
end;
← →
panov © (2004-11-16 14:41) [79]есть же TThreadList, можно же было или унаследоваться от него или включить его как член класса TPushPop
Ну не нравится мне этот класс, к сожалению.
Не лежит душа к использованию его.
← →
Digitman © (2004-11-16 14:42) [80]ну здрасть-приехали !
а это что
function TSafeArray.GetItem(const Index: Integer): Pointer;
begin
Lock;
Result := FArray[Index];
Unlock;
end;
?
← →
panov © (2004-11-16 14:43) [81]>Digitman © (16.11.04 14:11) [77]
опять же и TSafeArray это касаемо ...
не вижу резона вводить thread-safe-дин.массив вместо стандартного TThreadList .. к тому же бесконечные реаллокации памяти при операциях с дин.массивом ощутимо повлияют на общую производительность алгоритма..
В динамическом массиве(в пуле) каждый элемент создается один раз и живет до завершения работы пула.
← →
panov © (2004-11-16 14:44) [82]Digitman © (16.11.04 14:42) [80]
Точно.
Хотя я считал эту операцию все-таки безопасной. Но вглядевшись, вижу, что в данном контексте это не так-)
← →
Digitman © (2004-11-16 14:45) [83]
> не нравится мне этот класс, к сожалению
ну на нет и суда нет... хоть, imho, и напрасно
← →
Digitman © (2004-11-16 14:49) [84]
> panov © (16.11.04 14:43) [81]
> В динамическом массиве(в пуле) каждый элемент создается
> один раз и живет до завершения работы пула.
да неважно вдан.случае, кто, когда и сколько живет ..
важно что операции с дин.массивом по эффективности уступают аналогичным по логике операциям со списком-наследником TList
← →
panov © (2004-11-16 14:58) [85]Digitman © (16.11.04 14:49) [84]
Подумаю о замене массива на TThreadList
← →
Владислав © (2004-11-16 14:59) [86]ИМХО, много ненужного.
← →
panov © (2004-11-16 15:00) [87]Для Unlock сделал безусловное выполнение.
← →
panov © (2004-11-16 15:10) [88]>Владислав © (16.11.04 14:59) [86]
> ИМХО, много ненужного.
?
← →
Владислав © (2004-11-16 16:30) [89]Александр, я же бросал в Вашу ветку пул потоков.
Я без претензий на истину в последней инстанции, конечно, но его можно легко адаптировать и под Вашу задачу.
Страницы: 1 2 3 вся ветка
Форум: "Потрепаться";
Текущий архив: 2004.12.05;
Скачать: [xml.tar.bz2];
Память: 0.79 MB
Время: 0.041 c