![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Возможно ли при создании потока(нити) передать в него функцию
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, сколько раз говорилось уже на эту тему, все без толку. А потом жалуемся мол "прога тормозит", блин ну нет слов просто.