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

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


 
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
1-1100855579
xkiller
2004-11-19 12:12
2004.12.05
Я пишу свой компонент, меню.


1-1101307555
ceval
2004-11-24 17:45
2004.12.05
подскажите как затемнить кнопку Восстановить у формы


1-1100968318
Ivolg
2004-11-20 19:31
2004.12.05
ListView


8-1094150415
AKM
2004-09-02 22:40
2004.12.05
Чем можно avi шки проигрывать, кроме TMediaPlayer a ?


8-1094480779
hamster
2004-09-06 18:26
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
Английский Французский Немецкий Итальянский Португальский Русский Испанский