Клубове Дир.бг
powered by diri.bg
търси в Клубове diri.bg Разширено търсене

Вход
Име
Парола

Клубове
Dir.bg
Взаимопомощ
Горещи теми
Компютри и Интернет
Контакти
Култура и изкуство
Мнения
Наука
Политика, Свят
Спорт
Техника
Градове
Религия и мистика
Фен клубове
Хоби, Развлечения
Общества
Я, архивите са живи
Клубове Дирене Регистрация Кой е тук Въпроси Списък Купувам / Продавам 01:51 18.05.24 
Компютри и Интернет
   >> Delphi
Всички теми Следваща тема *Кратък преглед

Тема actions, threads, chain-of-actionsнови  
Авторtikva (Нерегистриран)
Публикувано15.06.07 14:18



Някой ако може да даде идея ще е супер.

======== целта: ===============
1) имам един timer, който периодично искам да извиква един "chain-of-actions" -
т.е. няколко TAction-a, които искам да се изпълнят последователно. Всеки от тези actions се обръща към един singleton (core) и му казва да прави нешо. този клас извършва някакво действие.

2) Във всеки един момент user-a може да запали някои от други 2 actions, които не са част от тази верига.
Когато това се случи искам:
- веригата да се "прекъсне" - т.е. като се изпълни поредния action да не се извика следващия.
- да се изпълни този "user" action
- да се пусне веригата отначало.

======= (опит за) реализация: ==========
1) Действията, които извършва cora са доста времеемки. затова съм ги направил в отделен thread. Като приключи действието се пали event - и всъщност този event показва, че дадения action е приключил и се стартира следващия от веригата (ако има).

2) При стартиране на веригата правя enabled := false на timera, за да не се окаже, че ще "светне" пак преди да е завършила веригата. в края правя enabled := true

всичко си изглежда ок, докато се намеси user-a :) малко се омотват нещата.

========== въпросчета: ============
- някой правил ли е нещо подобно и може ли да даде съвети ?
- по принцип onExecute() на даден action пали ли друг thread или се изпълнява в main-threada ?
- какво става ако се изпълнява даден код в main-threada и докато се изпълнява се пали някакъв друг action ?

a1.execute()
................... <=================== user action
a1.finishEvent() =
|
a2.execute() <=
....
a2.finishEvent()
..
..
..



Тема P.S: actions, threads, chain-of-actionsнови [re: tikva]  
Авторtikva (Нерегистриран)
Публикувано15.06.07 16:28



и изобщо по принцип като се изпълни някакв action или се запали някакъв event къде се "вмъква" изпълнението им ако са в main-threada ?

Ако имам един метод, който се изпълнява сравнително дълго време.
и ДОКАТО той се изпълнява се пали някакъв event или някой action от UI-то
тогава къде ще се "впише" изпълнението на този event или action ?
ще се изпълни ли метода докрай преди да се предаде управлението на event-a / action-a ?


MyMethod()
begin
.....
..... <============= action / event
.....
.....
end;



Тема По време на дълги процесинови [re: tikva]  
Автор NikB (любопитен)
Публикувано16.06.07 18:50



По време на дълги процеси не се обработва опашката със съобщения, освен, ако изрично не викаш Application.ProcessMessages.
Така например, ако имаш таймер, беговото събитие ОнТимер няма да се извика, докато не спре дългата ти обработка.



Тема Става пожарнови [re: tikva]  
Авторnop (Нерегистриран)
Публикувано16.06.07 19:27



Принципно, поне според документацията нито action-ите се палят,
нито thread-ите, още по-малко пък event-ите.

"- по принцип onExecute() на даден action пали ли друг thread или се изпълнява в main-threada ?"

Action-ите са си нормални процедури, които в общия случай (случая, в който не ги "извикваш" директно от друго място в кода) се държат като стандартни message-handlers на WM_COMMAND. Така, че те в никакъв случай не създават допълнителни нишки и се изпълняват в контекста на извикващото разклонение, обикновено main.

"- какво става ако се изпълнява даден код в main-threada и докато се изпълнява се пали някакъв
друг action ?"

Генерира се съобщението WM_COMMAND, изпраща се през SendMessage и го прихваща WindowProc на джама. Ако процедурата "намери" необходимия и handler (action) го изпълнява, пак в контеста на main. Ако не го намери го подава на DefWindowProc или връща стойност различна от нула.

"и изобщо по принцип като се изпълни някакв action или се запали някакъв event къде се "вмъква" изпълнението им ако са в main-threada ?

Ако имам един метод, който се изпълнява сравнително дълго време.
и докато той се изпълнява се пали някакъв event или някой action от UI-то
тогава къде ще се "впише" изпълнението на този event или action ?
ще се изпълни ли метода докрай преди да се предаде управлението на event-a / action-a ?"

Както ти написах по-горе сендва се WM_COMMAND. Ако през това време ти си в "дългия" метод и в тоя метод никъде не проверяваш опашката за новопостъпили съобщения - GetMessage, PeekMessage,
MsgWaitForMultipleObjects, (за Делфи - Application.ProcessMessages) джама ти спира да кореспондира. Т.е. action-а, който би трябвало да се изпълни ще чака да "излезеш" от дългия или от веригата дълги методи. Това, разбира се в случай, че веригата е контекста на главния.

Доколкото разбрах ти си изнесъл тая верига в отделна нишка, в такъв случай трябва да помислиш върху синхронизацията между двете - първо главния да разбере къде в момента е вторичния и вторичния да знае дали да спре и да почне от ново. За едното може да ползваш една елементарна LONG-променлива (LongInt) преди да пуснеш вторичната нишка = 0,
после преди(след) всяка операция - InterlockedIncrement()/Inc()/.

Не лоша идея е да набуташ action-ите в един масив и тоя LONG да ти се явява индекс на следващото за изпълнение действие. Ако индекса случайно стане -1 /InterlockedExchange/ туй ще рече че трябва да спреш.

За второто - много е важно какви операции ще извършва second-thread-а, най-вече какви ресурси ще заделя - например памет. Вариантите ти са поне два:

а) на всеки няколко реда в "дългите" методи да проверяваш нещо от сорта
на BOOL bRestartNeeded за TRUE, може и направо if (Terminated) ако ще създаваш нишката всеки път и да прекратяваш, сигнализирайки някакво събитие. Може WaitForSingleObject( MyThread.Handle, INFINITE ), но при всички случаи приложението ти ще hung-ва. Е, може да врътнеш един цикъл:

MyThread->Terminate();

while ( WAIT_TIMEOUT == WaitForSingleObject( (HANDLE)MyThread.Handle, 50 /*0*/ )
{
Application->ProcessMessages();
}

като не забравиш да "заключиш" влизането тука.

б) другия вариант - без чакане, е цялата памет и ресурс необходими за функционирането на вторичното разклонение да ги заделиш още в main, преди да го създадеш. И като ти възникне user-event дето трябва да рестартира веригата:

SuspendThread( (HANDLE)MyThread.Handle );
TerminateThread( (HANDLE)MyThread.Handle, 0 );
FreeThreadRequiredResources();

DoThisUserAction();

// Ако приемем, че на OnTimer ще създаваш нишката наново.
Timer->Enabled = true;

Надявам се да съм бил полезен.



Тема Re: Става пожарнови [re: nop]  
Авторtikva (Нерегистриран)
Публикувано18.06.07 11:13



Wow, мерси за изчерпателното мнение :)
ще го изчета още няколко пати внимателно, но и сега получих отговор на основните ми въпроси :)

по въпроса за "веригата"

main-thread || separate thread
MainCode | core ||
на практика MainCode и core
са в един thread.

chain: array of actions
continueChain: boolean

executeNextInChain()
тук проверявам дали continueChain e true
и ако е изпълнявам поредния action:
MainCode.action ==> core.doSomething || ----> doSomething пуска нов thread
междувременно executeNextInChain завършва.
след известно време
MainCode <=== core.Done || <--- като завърши thread-a има event

В момента се опитвам при действие от страна на user-a да сложа флага на false и просто да отбележа кой е този userAction.
в executeNextInChain ако флага е false да изпълня този userAction.
в userAction слагам флага пак на true и стартирам първия action от "веригата".

все още имам проблеми, но мисля че би трябвало така да се получи, пък и на първи прочит мисля че и ти предлагаш нещо подобно.

пак мерси за подробните обяснения :)



Тема Re: actions, threads, chain-of-actionsнови [re: tikva]  
Авторtikva (Нерегистриран)
Публикувано22.06.07 14:49



някой има ли желание да погледне и да ми направи едно кратко code review :)
видимо работи, обаче незнам дали съм сложил данните, които се ползват от main и от runner thread-а на правилното място (в случая - глобални:) и т.н.


unit ActionRunner;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils, SyncObjs, ActnList;

type
TClientActionCallback = procedure (const result: boolean) of object;

{ TClientAction }

TClientAction = class
action: TAction;
callback: TClientActionCallback;

constructor Create(const a: TAction; const cbk: TClientActionCallback);
end;

TClientActionChain = array of TClientAction;

{ TClientActionRunnerThread }

TClientActionRunnerThread = class(TThread)
procedure Execute; override;

private
finished: boolean;

function GetNextInChain(): TClientAction;
end;

{ TClientActionRunner }

TClientActionRunner = class
public
constructor Create;
destructor Destroy; override;

procedure InitChain(const ch: TClientActionChain);
procedure StartChain;

procedure SetUserAction(const ua: TClientAction);

Done: procedure(Sender: TObject) of object;
private
runnerThread: TClientActionRunnerThread;
end;

var
actrunLock: TCriticalSection;
chain: TClientActionChain;
chainIndex: integer;

userAction: TClientAction;

implementation

{ TClientActionRunner }

constructor TClientActionRunner.Create;
begin
actrunLock := TCriticalSection.Create;
end;

destructor TClientActionRunner.Destroy;
begin
FreeAndNil(actrunLock);
inherited Destroy;
end;

procedure TClientActionRunner.InitChain(const ch: TClientActionChain);
begin
chain := ch;
end;

procedure TClientActionRunner.StartChain;
begin
chainIndex := -1;
runnerThread := TClientActionRunnerThread.Create(true);
runnerThread.FreeOnTerminate := true;
runnerThread.OnTerminate := Done;
runnerThread.Resume;
end;

procedure TClientActionRunner.SetUserAction(const ua: TClientAction);
begin
actrunLock.Enter;
try
userAction := ua;
chainIndex := -1;
finally
actrunLock.Leave;
end;
end;

{ TClientActionRunnerThread }

function TClientActionRunnerThread.GetNextInChain(): TClientAction;
begin
result := nil;
actrunLock.Enter;
try
if (chainIndex = -1) then
begin
if (userAction <> nil) then
begin
result := userAction;
userAction := nil;
exit;
end
end;

Inc(chainIndex);
if (chainIndex < Length(chain)) then
result := chain[chainIndex];
finally
actrunLock.Leave;
end;
end;

procedure TClientActionRunnerThread.Execute;
var
ca: TClientAction;
ok: boolean;
begin
while (not terminated) do
begin
ca := GetNextInChain();
if (ca = nil) then // no next action - i.e. chain finished
break;

ok := ca.action.Execute();
ca.callback(ok);
end;
end;

{ TClientAction }

constructor TClientAction.Create(const a: TAction;
const cbk: TClientActionCallback);
begin
action := a;
callback := cbk;
end;

end.




a това е тестовата формичка, от която се ползва:


unit chainMain;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ActnList,
StdCtrls, ExtCtrls, Buttons;

type

{ TForm1 }

TForm1 = class(TForm)
aFinishRefreshing: TAction;
aRefreshStep5: TAction;
aRefreshStep4: TAction;
aRefreshStep3: TAction;
aRefreshStep2: TAction;
aUserAction2: TAction;
aRefreshStep1: TAction;
aUserAction1: TAction;
ActionList1: TActionList;
btnUserAction1: TButton;
btnUserAction2: TButton;
Memo1: TMemo;
Timer1: TTimer;
procedure aFinishRefreshingExecute(Sender: TObject);
procedure aRefreshStep1Execute(Sender: TObject);
procedure aRefreshStep2Execute(Sender: TObject);
procedure aRefreshStep3Execute(Sender: TObject);
procedure aRefreshStep4Execute(Sender: TObject);
procedure aRefreshStep5Execute(Sender: TObject);
procedure aUserAction1Execute(Sender: TObject);
procedure aUserAction2Execute(Sender: TObject);
procedure btnUserAction1Click(Sender: TObject);
procedure btnUserAction2Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ private declarations }

procedure RefreshStep1Done(const result: boolean);
procedure RefreshStep2Done(const result: boolean);
procedure RefreshStep3Done(const result: boolean);
procedure RefreshStep4Done(const result: boolean);
procedure RefreshStep5Done(const result: boolean);
procedure RefreshingDone(Sender: TObject);

procedure UserAction1Done(const result: boolean);
procedure UserAction2Done(const result: boolean);
public
{ public declarations }
end;

var
Form1: TForm1;

implementation

{ TForm1 }
uses
ActionRunner;

var
runner: TClientActionRunner;

procedure TForm1.FormShow(Sender: TObject);
var
chain: TClientActionChain;
ca: TClientAction;
begin
SetLength(chain, 5);

ca := TClientAction.Create(aRefreshStep1, @RefreshStep1Done);
chain[0] := ca;
ca := TClientAction.Create(aRefreshStep2, @RefreshStep2Done);
chain[1] := ca;
ca := TClientAction.Create(aRefreshStep3, @RefreshStep3Done);
chain[2] := ca;
ca := TClientAction.Create(aRefreshStep4, @RefreshStep4Done);
chain[3] := ca;
ca := TClientAction.Create(aRefreshStep5, @RefreshStep5Done);
chain[4] := ca;
{
ca := TClientAction.Create(aFinishRefreshing, @FinishRefreshingDone);
chain[5] := ca;
}
runner := TClientActionRunner.Create;
runner.Done := @RefreshingDone;
runner.InitChain(chain);
end;

procedure TForm1.aRefreshStep1Execute(Sender: TObject);
begin
Memo1.Lines.Add('refreshstep1 start');
sleep(1000);
end;

procedure TForm1.aFinishRefreshingExecute(Sender: TObject);
begin
Memo1.Lines.Add('refreshing finished');
end;

procedure TForm1.aRefreshStep2Execute(Sender: TObject);
begin
Memo1.Lines.Add('refreshstep2 start');
sleep(2000);
end;

procedure TForm1.aRefreshStep3Execute(Sender: TObject);
begin
Memo1.Lines.Add('refreshstep3 start');
sleep(3000);
end;

procedure TForm1.aRefreshStep4Execute(Sender: TObject);
begin
Memo1.Lines.Add('refreshstep4 start');
sleep(4000);
end;

procedure TForm1.aRefreshStep5Execute(Sender: TObject);
begin
Memo1.Lines.Add('refreshstep5 start');
sleep(1000);
end;

procedure TForm1.aUserAction1Execute(Sender: TObject);
begin
Memo1.Lines.Add('user action 1 start');
sleep(2000);
end;

procedure TForm1.aUserAction2Execute(Sender: TObject);
begin
Memo1.Lines.Add('user action 2 start');
sleep(2000);
end;

procedure TForm1.btnUserAction1Click(Sender: TObject);
var
ca: TClientAction;
begin
ca := TClientAction.Create(aUserAction1, @UserAction1Done);
runner.SetUserAction(ca);
end;

procedure TForm1.btnUserAction2Click(Sender: TObject);
var
ca: TClientAction;
begin
ca := TClientAction.Create(aUserAction2, @UserAction2Done);
runner.SetUserAction(ca);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := false;
runner.StartChain;
end;

procedure TForm1.RefreshStep1Done(const result: boolean);
begin
Memo1.Lines.Add('refreshstep1 done');
end;

procedure TForm1.RefreshStep2Done(const result: boolean);
begin
Memo1.Lines.Add('refreshstep2 done');
end;

procedure TForm1.RefreshStep3Done(const result: boolean);
begin
Memo1.Lines.Add('refreshstep3 done');
end;

procedure TForm1.RefreshStep4Done(const result: boolean);
begin
Memo1.Lines.Add('refreshstep4 done');
end;

procedure TForm1.RefreshStep5Done(const result: boolean);
begin
Memo1.Lines.Add('refreshstep5 done');
end;

procedure TForm1.RefreshingDone(Sender: TObject);
var
en: boolean;
begin
Timer1.Enabled := true;
en := Timer1.Enabled;
end;

procedure TForm1.UserAction1Done(const result: boolean);
begin
Memo1.Lines.Add('user action 1 done');
end;

procedure TForm1.UserAction2Done(const result: boolean);
begin
Memo1.Lines.Add('user action 2 done');
end;

initialization
{$I chainMain.lrs}

end.






Тема Re: actions, threads, chain-of-actions [re: tikva]  
Автор Formal (незнаещ)
Публикувано26.06.07 10:20



Аз виждам 3 проблема тук:
1. По принцип е възможно да направиш InitChain докато върви runner thread-а, което може да има нежелани последици (може да се загубят actions)
2. Ако дойде един user action и ако (преди да е тръгнал да се изпълнява) дойде втори user action, първият ще се загуби.
3. В help-а пише, че за VCL controls не се гарантира, че са thread-safe и че ако бараш контроли, това да става само от главната нишка. Виж help-а за Synchronize().

За глобалните променливи:
В твоя случай ми се струва, че глобалните променливи би трябвало да принадлежат на TClientActionRunner (или поне да са в implementation частта). Тогава TClientActionRunnerThread трябва да има reference към TClientActionRunner. А пък ако няма да правиш повече от един TClientActionRunner, може да го махнеш и да направиш методите му нормални процедури.




Всички темиСледваща тема*Кратък преглед
Клуб :  


Clubs.dir.bg е форум за дискусии. Dir.bg не носи отговорност за съдържанието и достоверността на публикуваните в дискусиите материали.

Никаква част от съдържанието на тази страница не може да бъде репродуцирана, записвана или предавана под каквато и да е форма или по какъвто и да е повод без писменото съгласие на Dir.bg
За Забележки, коментари и предложения ползвайте формата за Обратна връзка | Мобилна версия | Потребителско споразумение
© 2006-2024 Dir.bg Всички права запазени.