Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 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

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



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

Форум: "Потрепаться";
Текущий архив: 2004.12.05;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.6 MB
Время: 0.033 c
1-1100872116
kaktus
2004-11-19 16:48
2004.12.05
Работа с вордовскими автозаменами.


1-1100890969
AlexHawk
2004-11-19 22:02
2004.12.05
апуск и останов таймера кнопками с клавы??


3-1099770704
kib
2004-11-06 22:51
2004.12.05
БД


1-1100853835
Ivolg
2004-11-19 11:43
2004.12.05
Данные


1-1100429494
Артем Владимирович
2004-11-14 13:51
2004.12.05
Как сделать приложение мультиплатформенным?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский