Главная страница
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

хм, да. Но это все таки тоже исключение. С этом методом надо очень осторожно оброщаться.



Страницы: 1 2 3 вся ветка

Текущий архив: 2004.12.05;
Скачать: CL | DM;

Наверх




Память: 0.59 MB
Время: 0.04 c
6-1096457916
Наташа
2004-09-29 15:38
2004.12.05
Как можно скачать файл при помощи IdHTTP ?


14-1100534252
Drakon
2004-11-15 18:57
2004.12.05
Разработки Winamp прекращены


1-1100702009
AlexxGold
2004-11-17 17:33
2004.12.05
Перевод ресурсов делфи


1-1101112187
Ega23
2004-11-22 11:29
2004.12.05
TRxSpeedButton в триггерном режиме


14-1100801242
Quath
2004-11-18 21:07
2004.12.05
Мастерам словесного поноса!!! Читать обязательно!!!