Форум: "Основная";
Текущий архив: 2002.12.19;
Скачать: [xml.tar.bz2];
ВнизВозможно ли при создании потока(нити) передать в него функцию Найти похожие ветки
← →
v-o-y-a-g-e-r (2002-12-09 13:42) [0]Возможно ли при создании потока(нити) передать в него функцию что бы она выполнилась ?
Ситуация : есть класс с методами. Некоторые методы выполняют расчеты и очень тормозят систему.
Требуется сделать что то типа шаблона нити чтобы любой метод можно было передать в него при ее создании и он мог выполниться в отдельном потоке.
← →
Polevi (2002-12-09 14:21) [1]адрес передай
← →
Digitman (2002-12-09 14:27) [2]см. TMethod
← →
VaS (2002-12-09 14:29) [3]
type
TForm1 = class(TForm)
...
public
procedure DoWork;
end;
TObjProc = procedure of object;
TTr = class(TThread)
private
FProc: TObjProc;
procedure DoProc;
protected
procedure Execute; override;
public
constructor Create(Proc: TObjProc);
end;
implementation
procedure TForm1.Button1Click(Sender: TObject);
begin
TTr.Create(DoWork);
end;
constructor TTr.Create(Proc: TObjProc);
begin
inherited Create(true);
FProc:=Proc;
FreeOnTerminate:=true;
Resume;
end;
procedure TTr.DoProc;
begin
if Assigned(FProc) then
FProc;
end;
procedure TTr.Execute;
begin
Synchronize(DoProc);
end;
procedure TForm1.DoWork;
begin
Label1.Caption:=IntToStr(GetTickCount);
end;
Методы должны быть одного типа. Ну и synchronize() можно убрать, если методы не обращаются к интерфейсу.
← →
v-o-y-a-g-e-r (2002-12-09 14:41) [4]Vas, спасибо, я так уже успел поробовать...загвоздка встала в методах возвращающих результат - функциях...с процедурами все вроде бы пашет, но не знаю как выдрать возвращаемое функцией значение ... мож объяснишь..
← →
VaS (2002-12-09 15:09) [5]Можно по завершении вызывать callback-функцию объекта, тоже передаваемую в нить. В эту функцию передавать результат выполнения метода и нек. ID выполненного метода (который тоже передается в нить). А уж эта callback-функция обрабатывает результат на основе ID.
Я делал все это немного иначе - завел в классе thread-safe очередь, куда помещал ID _операций_, которые нужно выполнить (напр. opAddNewRecords - указание выполнить метод AddNewRec и т.д.). Далее в постоянно живущей глобальной нити разбирал очереди объектов и выполнял соответствующие методы.
← →
v-o-y-a-g-e-r (2002-12-09 15:30) [6]Vas, для меня не важно какой метод вызывался для меня важен просто результат вызова - число и по этому ID не так актуальны..., единственное что я вижу... сделать переменную , которую может эта callback функция просто апдейтить....?
Еще вопросик ... все функции подлежат обязательной типизации для вызова или можно так :
TObjFunc = function : variant of object;
← →
v-o-y-a-g-e-r (2002-12-09 15:34) [7]проще...
надо описать вызов функций и процедур к примеру :
function q ( ll : Integer ; ii : Integer ; S1 : Boolean ; S2 : Boolean ): Double ;
procedure SetTransferValue ( iType : Integer ;
iDevGroup1 : Integer ;
iDevGroup2 : Integer ;
Value : Double ) ;
function ShowTransfer ( iType : Integer ; Grid : TStringGrid ):Boolean ;
procedure ShowGroup ( iType : Integer ; StringGrid : TStringGrid );
надо создавать overload ...иначе конструктор нити не понимает параметров ?
← →
Polevi (2002-12-09 15:48) [8]>VaS © (09.12.02 14:29)
>procedure TTr.Execute;
>begin
> Synchronize(DoProc);
>end;
За такие советы надо убивать, без сожаления.
Этож надо весь код в основном потоке выполнять через Synchronize, сколько раз говорилось уже на эту тему, все без толку. А потом жалуемся мол "прога тормозит", блин ну нет слов просто.
← →
VaS (2002-12-09 15:53) [9]Но как ты будешь в нити вызывать методы с разными сигнатурами? Я решения пока не вижу. Только создать по классу нити на каждую сигнатуру метода...
← →
VaS (2002-12-09 16:01) [10]Polevi:
Да, ты прав. Это я для теста Label обновлял в synchronize(). Смысла в таком коде действительно нет. Но убивать меня не надо.
← →
v-o-y-a-g-e-r (2002-12-09 16:07) [11]тааак....приехали...ладно под сигнатуры я подведу методы ужму или еще что придумаю...и сделаю Create - overload. А как в таком случае быть с дальнейшим определением procedure ....DoProc(???);
← →
VaS (2002-12-09 16:49) [12]DoProc не нужен :) Я его ввел для этого чертова synchronize(). Т.е. в Execute() вызываем FProc.
Но вообще-то лучше делать так: задаем состояние объекта (присваиваем значения полям) и выполняем один из методов (процедур без параметров) в нитке. Этот метод модифицирует объект, т.е. берет значения одних полей и изменяет другие поля. И нет проблем с сигнатурами. Только при этом может выполняться одновременно лишь один метод, для сохранения целостности объекта. Критические секции тебе в руки.
← →
Polevi (2002-12-09 17:04) [13]//структура для 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;
← →
Polevi (2002-12-09 17:10) [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;
← →
v-o-y-a-g-e-r (2002-12-09 17:25) [15]Polevi, спасибо за материал . попробую обязательно. В борландовском описании, TMethod описан достаточно скудно и просмотрев по сообщению Digitman"а его(TMethod) описание не уловил что с ним делать... так как ни разу не использовал...
а с Вашим примером возможно что и выгорит... правда придется перетащить в структуру штук пять матриц...:(((
← →
v-o-y-a-g-e-r (2002-12-09 17:34) [16]Polevi, не вышло ... требовалось модифицировать сам объект, знать типы и иметь доступ к внутренним хранилищам объекта...
а если посмотреть на это с другой стороны...
Есть объект , с полностью реализованной системой методов.
Требуется:
1. Передать в поток объект.
2. Передать в поток идентификатор метода и переменные.
3. Запустить выполняться метод.
4. Дождаться выполнения и получить результат.
Такое возможно реализовать...
← →
VaS (2002-12-09 17:47) [17]> // ждем завершения, но в принципе поток может сам уведомить
> // о готовности данных
> w1.WaitFor;
> w2.WaitFor;
Замечательно :) Пока мы ждем, очередь сообщений главной нити что делает?.. Правильно, не обрабатывается.
Также интересно использование поля TMethod.Data :) Вообще-то оно должно содержать указатель на объект класса, а Code - на метод в VTable класса.
Кроме того, человек ясно сказал, что ему нужно вызывать не глобальные функции, а методы объекта.
Автору: Присмотрись все же к моей последней идее насчет состояния объекта.
← →
v-o-y-a-g-e-r (2002-12-09 18:07) [18]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"
в общем то все ясно...
но как избавиться то ?
← →
v-o-y-a-g-e-r (2002-12-09 18:16) [19]предыдущее сообщение неверно...работает все...но если передаешь без параметров...
запустить чтото типа
TObjFunc = function(VarI : Integer ; VarS : String) : integer of object;
пока не удалось...никак не разберусь каким образом выполнить такую функцию.
← →
Polevi (2002-12-09 19:01) [20]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.
← →
Николай Быков (2002-12-09 19:05) [21]
> v-o-y-a-g-e-r
Скажи, где ты взял доки/статьи про TThread.
Мне тоже нужно
← →
v-o-y-a-g-e-r (2002-12-09 19:17) [22]>Николай Быков
где брал не помню...рыл инет яндекс, рамблер, гугл...если есть мыло...давай..вышлю что есть..как все сгребу..
← →
v-o-y-a-g-e-r (2002-12-09 19:21) [23]>Николай Быков
сори мыло вижу...просто привык что оно на нике..
← →
VaS (2002-12-09 22:13) [24]
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.
Методы будут выполняться последовательно (а как иначе? мы же изменяем объект). Поэтому проще будет та самая очередь операций, о которой я писал с единственной нитью.
← →
Polevi (2002-12-10 09:28) [25]>VaS © (09.12.02 22:13)
мой способ объективно лучше, без обид :-)
← →
VaS (2002-12-10 10:00) [26]Polevi:
Для каждой новой сигнатуры метода создание дочернего от TProc класса - тежеловесно.
Сложность слежения за временем жизни этих объектиков (TProc) - например в твоем примере имеем утечку памяти :)
Нарушение основной идеи ООП - инкапсуляция данных. В моем варианте данные неотделимы от объекта, который их и обрабатывает. А в твоем ты будешь брать данные из некого объекта (видимо, т.к. именно это нужно автору), алгоритм и выполнение - из другого, результат вообще повисает неизвестно где вместе с объектом, если третий объект (форма) не обработает результат и, может быть, удалит его.
В целом - жутко ненадежно.
← →
Polevi (2002-12-10 10:15) [27]так так..
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
а что касается что будет если не обработает, удалит и тд - этак можно любой способ признать негодным, и твой в том числе
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2002.12.19;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.009 c