Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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
3-61392
Павка
2002-12-03 04:43
2002.12.19
Как заполнить шаблон Вордовский (справка) данными из базы


8-61712
Sniffer
2002-09-04 17:21
2002.12.19
OpenGL и перспектива


4-61890
Xemax
2002-11-02 15:31
2002.12.19
Время задавать ОЧЕНЬ СЛОЖНЫЕ ВОПРОСЫ !!!!


3-61496
berezne
2002-12-02 14:14
2002.12.19
Открытие DBF файла без индекса


1-61522
viper
2002-12-09 12:54
2002.12.19
Как закрыть форму?





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский