Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.12.05;
Скачать: CL | DM;

Вниз

Нужна критика кода-)   Найти похожие ветки 

 
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;
Скачать: CL | DM;

Наверх




Память: 0.79 MB
Время: 0.026 c
3-1099897472
karat
2004-11-08 10:04
2004.12.05
Про работу SQL серверов и возвращаемый набор данных.


14-1100382291
Fin
2004-11-14 00:44
2004.12.05
Создание серьезных игр.


3-1099983146
denis24
2004-11-09 09:52
2004.12.05
редактирование в гриде


14-1100520707
syte_ser78
2004-11-15 15:11
2004.12.05
Как я провел выходные


3-1099989334
Pavor
2004-11-09 11:35
2004.12.05
Самопроизвольная запись в таблицу DB???