Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Возможно ли при создании потока(нити) передать в него функцию


Страницы:   1 2

v-o-y-a-g-e-r ©   (2002-12-09 13:42:00) 

Возможно ли при создании потока(нити) передать в него функцию что бы она выполнилась ?

Ситуация : есть класс с методами. Некоторые методы выполняют расчеты и очень тормозят систему.
Требуется сделать что то типа шаблона нити чтобы любой метод можно было передать в него при ее создании и он мог выполниться в отдельном потоке.


Polevi   (2002-12-10 10:15:00) [1]

так так..

procedure TWorker.Execute;
begin
FProc.Execute;
SendMessage(FWnd,FMessage,Integer(FProc),0);
end;

обрабатываем сообщение, TProc(Msg.wParam).Free - и нет утечки
в TWorker FreeOnTerminate:=true - и нет утечки
SendMessage можно заменить на PostMessage

а что касается "моем варианте данные неотделимы от объекта" - позвольте, мой объект-функция как раз так и работает - есть данные-члены, есть метод Execute, который с ними работает

и если в TProc добавить конструктор, то асинхронный вызов ф-ии будет выглядеть как
TWorker.Create(false,TProc1.Create(1,2),Handle,DONE_MESSAGE)
очень удобно, по моему, причем вызовов может быть сколько угодно, и не надо никакой синхронизации

>результат вообще повисает неизвестно где вместе с объектом, >если третий объект (форма) не обработает результат и, может >быть, удалит его.

во первых третий объект это не форма а окно windows, и то что объект хранит в себе результат своей работы очень логично, IMHO

а что касается что будет если не обработает, удалит и тд - этак можно любой способ признать негодным, и твой в том числе



VaS   (2002-12-10 10:00:00) [2]

Polevi:
Для каждой новой сигнатуры метода создание дочернего от TProc класса - тежеловесно.
Сложность слежения за временем жизни этих объектиков (TProc) - например в твоем примере имеем утечку памяти :)
Нарушение основной идеи ООП - инкапсуляция данных. В моем варианте данные неотделимы от объекта, который их и обрабатывает. А в твоем ты будешь брать данные из некого объекта (видимо, т.к. именно это нужно автору), алгоритм и выполнение - из другого, результат вообще повисает неизвестно где вместе с объектом, если третий объект (форма) не обработает результат и, может быть, удалит его.
В целом - жутко ненадежно.


Polevi   (2002-12-10 09:28:00) [3]

>VaS © (09.12.02 22:13)
мой способ объективно лучше, без обид :-)


VaS   (2002-12-09 22:13:00) [4]


type
TMy = class
private
CS : TRTLCriticalSection;
FRes : Variant;
Fi : integer;
Fd : double;
Fs : string;
function GetRes: Variant;
public
constructor Create;
destructor Destroy; override;
procedure Lock;
procedure UnLock;
procedure Proc1;
procedure Proc2;
property Res : Variant read GetRes;
property i : integer write Fi;
property d : double write Fd;
property s : string write Fs;
end;

TObjProc = procedure of object;

TTr = class(TThread)
private
FProc: TObjProc;
protected
procedure Execute; override;
public
constructor Create(Proc: TObjProc);
end;

implementation

var
My: TMy = nil;

procedure TForm2.Button1Click(Sender: TObject);
begin
My:=TMy.Create;
My.i:=23;
My.d:=34.5;
TTr.Create(My.Proc1);
My.s:="sss";
TTr.Create(My.Proc2);
end;

{ TTr }

constructor TTr.Create(Proc: TObjProc);
begin
inherited Create(false);
FProc:=Proc;
FreeOnTerminate:=true;
Resume;
end;

procedure TTr.Execute;
begin
FProc;
end;

{ TMy }

constructor TMy.Create;
begin
InitializeCriticalSection(CS);
end;

destructor TMy.Destroy;
begin
DeleteCriticalSection(CS);
inherited;
end;

procedure TMy.Lock;
begin
EnterCriticalSection(CS);
end;

procedure TMy.Proc1;
begin
Lock;
try
FRes:=Fi+Fd;
finally
UnLock;
end;
end;

procedure TMy.Proc2;
begin
Lock;
try
FRes:=IntToStr(Fi)+Fs;
finally
UnLock;
end;
end;

function TMy.GetRes: Variant;
begin
Lock;
try
Result:=FRes;
finally
UnLock;
end;
end;

procedure TMy.UnLock;
begin
LeaveCriticalSection(CS);
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
//узнаем текущий результат
if My <> nil then
ShowMessage(My.Res);
end;

initialization
finalization
FreeAndNil(My);
end.


Методы будут выполняться последовательно (а как иначе? мы же изменяем объект). Поэтому проще будет та самая очередь операций, о которой я писал с единственной нитью.


v-o-y-a-g-e-r   (2002-12-09 19:21:00) [5]

>Николай Быков
сори мыло вижу...просто привык что оно на нике..


v-o-y-a-g-e-r   (2002-12-09 19:17:00) [6]

>Николай Быков
где брал не помню...рыл инет яндекс, рамблер, гугл...если есть мыло...давай..вышлю что есть..как все сгребу..


Николай Быков   (2002-12-09 19:05:00) [7]


> v-o-y-a-g-e-r

Скажи, где ты взял доки/статьи про TThread.
Мне тоже нужно


Polevi   (2002-12-09 19:01:00) [8]

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

const
DONE_MESSAGE=WM_USER+100;

type
TProc=class
public
Result:Variant;
procedure Execute;virtual;abstract;
end;

TProc1=class(TProc)
public
Field1,Field2:integer;
procedure Execute;override;
end;

TProc2=class(TProc)
public
Field1,Field2:double;
procedure Execute;override;
end;

TWorker=class(TThread)
public
FWnd:THandle;
FMessage:Cardinal;
FProc:TProc;
constructor Create(CreateSuspended:boolean;AProc:TProc;AWnd:THandle;AMessage:Cardinal);reintroduce;
procedure Execute;override;
end;


type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure Done(var Msg:TMessage);message DONE_MESSAGE;
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Done(var Msg: TMessage);
var
s:string;
begin
s:=TProc(Msg.wParam).Result;
Memo1.Lines.Add(TProc(Msg.wParam).ClassName+" done.. Result="+s);
end;

{ TProc1 }

procedure TProc1.Execute;
begin
Result:=Field1*Field2;
end;

{ TProc2 }

procedure TProc2.Execute;
begin
Result:=Field1+Field2;
end;

{ TWorker }

constructor TWorker.Create(CreateSuspended: boolean; AProc: TProc;
AWnd: THandle; AMessage: Cardinal);
begin
FWnd:=AWnd;
FMessage:=AMessage;
FProc:=AProc;
inherited Create(CreateSuspended);
end;

procedure TWorker.Execute;
begin
FProc.Execute;
SendMessage(FWnd,FMessage,Integer(FProc),0);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
p1:TProc1;
p2:TProc2;
begin
p1:=TProc1.Create;
p1.Field1:=2;
p1.Field2:=3;
TWorker.Create(false,p1,Handle,DONE_MESSAGE);

p2:=TProc2.Create;
p2.Field1:=2.234;
p2.Field2:=3.234456;
TWorker.Create(false,p2,Handle,DONE_MESSAGE);
end;


end.


v-o-y-a-g-e-r   (2002-12-09 18:16:00) [9]

предыдущее сообщение неверно...работает все...но если передаешь без параметров...
запустить чтото типа
TObjFunc = function(VarI : Integer ; VarS : String) : integer of object;
пока не удалось...никак не разберусь каким образом выполнить такую функцию.


v-o-y-a-g-e-r   (2002-12-09 18:07:00) [10]

VaS
/Но вообще-то лучше делать так: задаем состояние объекта
/(присваиваем значения полям) и выполняем один из методов
/(процедур без параметров) в нитке. Этот метод модифицирует
/объект, т.е. берет значения одних полей и изменяет другие поля.
/И нет проблем с сигнатурами. Только при этом может выполняться
/одновременно лишь один метод, для сохранения целостности
/объекта. Критические секции тебе в руки.

Я так тоже начал модифицировать код... просто изменений еще надо кучу сделать чтоб результат получить...

я в общем то уже переделал все на вызов одного метода без параметров , но с возвращаемым результатом типа Integer...
объявил
TObjFunc = function : integer of object;

constructor Create( Func: TObjFunc );

далее по примеру

constructor TEThread.Create(Func: TObjFunc);
begin
inherited Create(True);
FFunc :=Func;
FreeOnTerminate :=True;
Resume;
end;


и теперь компилер ругается на
if Assigned(FFunc) then
Synchronize(FFunc);
что
[Error] EThreads.pas(78): Incompatible types: "procedure, untyped pointer or untyped parameter" and "Integer"
в общем то все ясно...

но как избавиться то ?


VaS   (2002-12-09 17:47:00) [11]

> // ждем завершения, но в принципе поток может сам уведомить
> // о готовности данных
> w1.WaitFor;
> w2.WaitFor;

Замечательно :) Пока мы ждем, очередь сообщений главной нити что делает?.. Правильно, не обрабатывается.

Также интересно использование поля TMethod.Data :) Вообще-то оно должно содержать указатель на объект класса, а Code - на метод в VTable класса.

Кроме того, человек ясно сказал, что ему нужно вызывать не глобальные функции, а методы объекта.

Автору: Присмотрись все же к моей последней идее насчет состояния объекта.


v-o-y-a-g-e-r   (2002-12-09 17:34:00) [12]

Polevi, не вышло ... требовалось модифицировать сам объект, знать типы и иметь доступ к внутренним хранилищам объекта...

а если посмотреть на это с другой стороны...
Есть объект , с полностью реализованной системой методов.

Требуется:
1. Передать в поток объект.
2. Передать в поток идентификатор метода и переменные.
3. Запустить выполняться метод.
4. Дождаться выполнения и получить результат.

Такое возможно реализовать...


v-o-y-a-g-e-r   (2002-12-09 17:25:00) [13]

Polevi, спасибо за материал . попробую обязательно. В борландовском описании, TMethod описан достаточно скудно и просмотрев по сообщению Digitman"а его(TMethod) описание не уловил что с ним делать... так как ни разу не использовал...
а с Вашим примером возможно что и выгорит... правда придется перетащить в структуру штук пять матриц...:(((


Polevi   (2002-12-09 17:10:00) [14]

сорри, ошибся

procedure TForm1.Button1Click(Sender: TObject);
var
Data1:^TData1;
Data2:^TData2;
m1,m2:TMethod;
w1,w2:TWorker;
begin
new(Data1); //первая структура
Data1.Value1:=8; //входной параметр
m1.Code:=@Proc1; //адрес ф-ии
m1.Data:=Data1; //адрес данных

new(Data2); //вторая структура
Data2.Value1:=1.2345; //первый входной параметр
Data2.Value2:=6.6790; //второй входной параметр
m2.Code:=@Proc2; //адрес ф-ии
m2.Data:=Data2; //адрес данных

w1:=TWorker.Create(false, m1);
w2:=TWorker.Create(false, m2);

// ждем завершения, но в принципе поток может сам уведомить
// о готовности данных

w1.WaitFor;
w2.WaitFor;

ShowMessage(IntToStr(Data1.Result));
ShowMessage(FloatToStr(Data2.Result));
end;


Polevi   (2002-12-09 17:04:00) [15]

//структура для Proc1
Type
TData1=record
Value1:integer;
Result:integer;
end;

PData1=^TData1;

//структура для Proc2
TData2=record
Value1:double;
Value2:double;
Result:double;
end;

PData2=^TData2;

TProc = procedure(Data:Pointer);

TWorker=class(TThread)
private
FMethod:TMethod;
public
constructor Create(CreateSuspended:boolean;AMethod:TMethod);reintroduce;
procedure Execute;override;
end;

procedure Proc1(Data:PData1);
procedure Proc2(Data:PData2);

implementation


procedure Proc1(Data: PData1);
begin
Data.Result:=Data.Value1*2; //просто умножаем на 2
end;

procedure Proc2(Data: PData2);
begin
Data.Result:=Data.Value1+Data.Value2; //складываем
end;

{ TWorker }

constructor TWorker.Create(CreateSuspended: boolean; AMethod:TMethod);
begin
FMethod:=AMethod;
inherited Create(CreateSuspended);
end;

procedure TWorker.Execute;
begin
TProc(FMethod.Code)(FMethod.Data); //вызываем наш метод
end;

procedure TForm1.Button1Click(Sender: TObject);
var
Data1:^TData1;
Data2:^TData2;
m:TMethod;
w1,w2:TWorker;
begin
new(Data1); //первая структура
Data1.Value1:=8; //входной параметр
m.Code:=@Proc1; //адрес ф-ии
m.Data:=Data1; //адрес данных

new(Data2); //вторая структура
Data2.Value1:=1.2345; //первый входной параметр
Data2.Value2:=6.6790; //второй входной параметр
m.Code:=@Proc2; //адрес ф-ии
m.Data:=Data2; //адрес данных

w1:=TWorker.Create(false, m);
w2:=TWorker.Create(false, m);

// ждем завершения, но в принципе поток может сам уведомить
// о готовности данных

w1.WaitFor;
w2.WaitFor;

ShowMessage(IntToStr(Data1.Result));
ShowMessage(FloatToStr(Data2.Result));
end;


VaS   (2002-12-09 16:49:00) [16]

DoProc не нужен :) Я его ввел для этого чертова synchronize(). Т.е. в Execute() вызываем FProc.
Но вообще-то лучше делать так: задаем состояние объекта (присваиваем значения полям) и выполняем один из методов (процедур без параметров) в нитке. Этот метод модифицирует объект, т.е. берет значения одних полей и изменяет другие поля. И нет проблем с сигнатурами. Только при этом может выполняться одновременно лишь один метод, для сохранения целостности объекта. Критические секции тебе в руки.


v-o-y-a-g-e-r   (2002-12-09 16:07:00) [17]

тааак....приехали...ладно под сигнатуры я подведу методы ужму или еще что придумаю...и сделаю Create - overload. А как в таком случае быть с дальнейшим определением procedure ....DoProc(???);


VaS   (2002-12-09 16:01:00) [18]

Polevi:
Да, ты прав. Это я для теста Label обновлял в synchronize(). Смысла в таком коде действительно нет. Но убивать меня не надо.


VaS   (2002-12-09 15:53:00) [19]

Но как ты будешь в нити вызывать методы с разными сигнатурами? Я решения пока не вижу. Только создать по классу нити на каждую сигнатуру метода...


Polevi   (2002-12-09 15:48:00) [20]

>VaS © (09.12.02 14:29)
>procedure TTr.Execute;
>begin
> Synchronize(DoProc);
>end;

За такие советы надо убивать, без сожаления.
Этож надо весь код в основном потоке выполнять через Synchronize, сколько раз говорилось уже на эту тему, все без толку. А потом жалуемся мол "прога тормозит", блин ну нет слов просто.


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо