![]() |
TTask Frage
Hallo Forum,
ich habe eine Frage zu TTask. Mein Task braucht länger wie der Task aufgerufen wird. Daraus resultiert ein Mehrfachaufruf des Tasks. Ich möchte einen mehrfachen Aufruf vermeiden, weiß nur nicht so wie ich das realisieren kann. Mit TTask.Wait blockiere ich ja meine App :shock: Vielen Dank schon mal für Eure tipps :-) Folgender Code:
Code:
Der Aufruf ist nun so:
(*** TMaschine *********************************************************************************************************************************************************************)
constructor TMaschine.Create(AOwner: TComponent); begin FOwner := AOwner; PollTimer := TTimer.Create(FOwner); PollTimer.Interval := FInterval; PollTimer.OnTimer := OnPollTimer; PollTimer.Enabled := False; end; destructor TMaschine.Destroy; begin PollTimer.Enabled := False; TTask.WaitForAll(OpcTask); inherited; end; procedure TMaschine.SetInterval(Value: Cardinal); begin FInterval := Value; PollTimer.Interval := Value; end; procedure TMaschine.OnPollTimer(Sender: TObject); begin ReadOpcData; end; procedure TMaschine.SetHostIp(Value: TIpAdresse); begin FHostIp := Value; end; procedure TMaschine.SetActive(Value: Boolean); begin FActive := Value; Maschinendaten.MaschineOnline := False; PollTimer.Enabled := Value; end; procedure TMaschine.ReadOpcData; begin OpcTask := TTask.Create(procedure()begin Inc(MaschinenDaten.TestCounter); OnOpcData; Delay(5000); // <- Ist nur ein "Zeitfresser", der die spätere OPC-Abfrage simuliert und länger ist wie das Timerinteval! end); OpcTask.Start; end; procedure TMaschine.OnOpcData; begin if Assigned(FOndata) then FOnData(Self); end;
Code:
procedure TForm1.FormShow(Sender: TObject); begin Maschine := TMaschine.Create(Self); try Maschine.Parent := Self.Parent; Maschine.OnData := OnOpcData; Maschine.PollInterval := 100; // <- Extra sehr kurz eingestellt um eine Zeitüberschneidung zu provozieren! Maschine.Active := True; except // TODO end; end; procedure TForm1.OnOpcData(Sender: TObject); begin Caption := (Sender as TMaschine).Maschinendaten.TestCounter.ToString; end; procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if Assigned(Maschine) then begin Maschine.Active := False; FreeAndNil(Maschine); end; end; |
AW: TTask Frage
Es fehlt ja das drumrum, deshabl kann ich nur raten:
1. Du rufst ja Deinen Task-Erzeuger ReadOpcData; anscheiend mit 100ms Intervallen auf
Delphi-Quellcode:
wobei Du im Task selber 5000ms wartest.
procedure TMaschine.OnPollTimer(Sender: TObject);
begin ReadOpcData; end;
Delphi-Quellcode:
Deshalb erzeugst Du nicht einen Task, sondern Myriaden Tasks, die 5 Sek. warten, wenn der Timer frei läuft (wovon ich mal ausgehe).
procedure TMaschine.ReadOpcData;
begin OpcTask := TTask.Create(procedure()begin Inc(MaschinenDaten.TestCounter); OnOpcData; Delay(5000); // <- Ist nur ein "Zeitfresser", der die spätere OPC-Abfrage simuliert und länger ist wie das Timerinteval! end); OpcTask.Start; end; Und deine Tasks laufen über ... 2. Aber abgesehen davon, ich vermute mal dass Du nur EINEN Task erzeugen darfst, denn Du hast ja auch nur EINE Quell-Schnittstelle die Dir Daten liefert. Deshalb solltest Du die Abfrage wohl irgendwie anders strukturieren. 3. Ich gehe davon aus dass in OnOpcData auch der UI-Thread angefasst wird. Das würde dann irgendwann crashen, deshalb müsste man in FOnData( Self ) eine Synchronisation einbauen (TThread.Queue, TThread.Synchronize).
Delphi-Quellcode:
procedure TMaschine.OnOpcData; begin if Assigned(FOndata) then FOnData(Self); end; 4. Anscheinend versuchst Du im FOnData( Self ) wieder auf die Daten im Thread zuzugreifen ( zu holen ), das muss auch besonders abgesichert werden, deshalb wäre es besser dem FOnData( Self, FMeineDaten ) direkt die Daten aus dem Thread zu übergeben. |
AW: TTask Frage
Hallo,
danke für die Tips wegen dem OnData. Das werde ich berücksichtigen. Aber zum Thema: Ich rufe bewusst mit 100ms auf und bummel 5 Sekunden im Task rum um das Problem zu provozieren. Wenn das so läuft, dann möchte ich den TTask nicht nochmal starten sondern den Aufruf so lange ignorieren, bis der vorherige Task fertig ist... Das gepostete Programm ist nur eine "Testversion", damit ich ein bisschen zum Posten hatte :-) |
AW: TTask Frage
Zitat:
aber Du erzeugst alle 100ms auch einen neuen Task, ohne auf irgendetwas zu warten, wenn ich das richtig sehe. Du könntest versuchen mit TEvent etwas im Task zu synchronisieren, oder auch die Aufrufe anzuhalten bis der aktuelle Task fertig ist. Du hast doch nur eine Datenquelle, oder nicht ? Da können sich doch nicht 100 Tasks gleichzeitig dranhängen ? |
AW: TTask Frage
Zitat:
UNIT:
Code:
unit Kommunikation; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.Threading, Vcl.Controls, Vcl.ExtCtrls; type TIpAdresse = string[15]; TMaschinenDaten = record MaschineOnline: Boolean; end; TOnDataEvent = procedure(Sender: TObject; Value: TMaschinendaten) of object; TMaschine = class(TObject) private FOwner: TComponent; FParent: TControl; FHostIP: TIpAdresse; FActive: Boolean; OpcTask: ITask; PollTimer: TTimer; FOnData: TOnDataEvent; FInterval: Cardinal; Maschinendaten: TMaschinendaten; procedure SetHostIp(Value: TIpAdresse); procedure SetActive(Value: Boolean); procedure SetInterval(Value: Cardinal); procedure ReadOpcData; procedure OnPollTimer(Sender: TObject); procedure OnOpcData(Sender: TObject; Value: TMaschinendaten); public constructor Create(AOwner: TComponent); virtual; destructor Destroy; override; property Parent: TControl read FParent write FParent; property HostIP: TIpAdresse read FHostIp write SetHostIp; property Active: Boolean read FActive write SetActive; property OnData: TOnDataEvent read FOnData write FOnData; property PollInterval: Cardinal read FInterval write SetInterval; end; implementation (*** TMaschine *********************************************************************************************************************************************************************) constructor TMaschine.Create(AOwner: TComponent); begin FOwner := AOwner; PollTimer := TTimer.Create(FOwner); PollTimer.Interval := FInterval; PollTimer.OnTimer := OnPollTimer; PollTimer.Enabled := False; end; destructor TMaschine.Destroy; begin PollTimer.Enabled := False; TTask.WaitForAll(OpcTask); inherited; end; procedure TMaschine.SetInterval(Value: Cardinal); begin FInterval := Value; PollTimer.Interval := Value; end; procedure TMaschine.OnPollTimer(Sender: TObject); begin ReadOpcData; end; procedure TMaschine.SetHostIp(Value: TIpAdresse); begin FHostIp := Value; end; procedure TMaschine.SetActive(Value: Boolean); begin FActive := Value; PollTimer.Enabled := Value; end; procedure TMaschine.ReadOpcData; begin PollTimer.Enabled := False; OpcTask := TTask.Create(procedure()begin {* HIER WERDEN DIE OPC-DATEN DANN GELESEN} OnOpcData(Self, Maschinendaten); PollTimer.Enabled := True; end); OpcTask.Start; end; procedure TMaschine.OnOpcData(Sender: TObject; Value: TMaschinendaten); begin if Assigned(FOndata) then FOnData(Self, Maschinendaten); end; end. AUFRUF:
Code:
unit MainForm; interface uses Kommunikation, Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls; type TfrmMain = class(TForm) procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormShow(Sender: TObject); private Maschine: TMaschine; MaschinenDaten: TMaschinendaten; procedure OnOpcData(Sender: TObject; Value: TMaschinendaten); public end; var frmMain: TfrmMain; implementation {$R *.dfm} procedure TfrmMain.FormShow(Sender: TObject); begin Maschine := TMaschine.Create(Self); try Maschine.Parent := Self.Parent; Maschine.OnData := OnOpcData; Maschine.PollInterval := 1000; Maschine.Active := True; except // TODO end; end; procedure TfrmMain.OnOpcData(Sender: TObject; Value: TMaschinendaten); begin MaschinenDaten := Value; end; procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if Assigned(Maschine) then begin Maschine.Active := False; FreeAndNil(Maschine); end; end; end. |
AW: TTask Frage
Spricht etwas dagegen einen Thread zu haben der im Hintergrund periodisch die Daten abruft und ein stinknormaler Getter liefert den zuletzt gelesenen Wert (und optional noch z.B. den Zeitstempel damit man sieht wie alt der ist)? Ist doch viel einfacher so.
|
AW: TTask Frage
Guten Morgen Günter,
Nein dagegen spricht auch nichts. Ich bin ja noch in der Denk- und Planungsphase. Deshalb frage ich ja hier um eine optimalen Ansatz zu finden. |
AW: TTask Frage
Hallo,
ich komme irgendwie nicht so klar mit TTask/TThread. Meine Probleme sind: 1. ReadOpcData soll in einen Thread, damit mein Mainprogramm weiter läuft. 2. Wenn ich dann die Maschinen freigebe (Mainprogramm wird beendet) müssen die Tasks/bzw. Threads dann sauber beendet werden. Ich lande aber irgendwie immer wieder im Chaos (Exceptions, MemoryLeaks etc.) Vielleicht hat ja jemand einen guten Tip, wie ich das am besten Lösen kann... Die TMaschine ist so definiert:
Code:
Mein Hauptprogramm ist in etwa das:
unit Kommunikation;
interface uses Globals, DataModul, tbUtils, Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.Threading, Vcl.Controls, Vcl.ExtCtrls, Vcl.Forms; type TMaschinenDaten = record MaschineOnline: Boolean; end; TOnDataEvent = procedure(Sender: TObject; Value: TMaschinendaten) of object; TMaschine = class(TObject) private FOwner: TComponent; FHostIP: WideString; FActive: Boolean; PollTimer: TTimer; FOpcTask: ITask; FOnData: TOnDataEvent; FOnTaskStart: TNotifyEvent; FOnTaskReady: TNotifyEvent; FInterval: Cardinal; FMaschinenID: Integer; FMaschinenName: WideString; FMaschinendaten: TMaschinendaten; procedure SetHostIp(Value: WideString); procedure SetActive(Value: Boolean); procedure SetInterval(Value: Cardinal); procedure ReadOpcData; procedure OnPollTimer(Sender: TObject); procedure OnOpcData(Sender: TObject; Value: TMaschinendaten); procedure OnOpcTaskStart(Sender: TObject); procedure OnOpcTaskReady(Sender: TObject); public constructor Create(AOwner: TComponent); virtual; destructor Destroy; override; property HostIP: WideString read FHostIp write SetHostIp; property Active: Boolean read FActive write SetActive; property OnData: TOnDataEvent read FOnData write FOnData; property OnTaskStart: TNotifyEvent read FOnTaskStart write FOnTaskStart; property OnTaskReady: TNotifyEvent read FOnTaskReady write FOnTaskReady; property PollInterval: Cardinal read FInterval write SetInterval; property MaschinenID: Integer read FMaschinenID write FMaschinenID; property MaschinenName: WideString read FMaschinenName write FMaschinenName; end; implementation (*** TMaschine *********************************************************************************************************************************************************************) constructor TMaschine.Create(AOwner: TComponent); begin FOwner := AOwner; PollTimer := TTimer.Create(FOwner); PollTimer.Interval := FInterval; PollTimer.OnTimer := OnPollTimer; PollTimer.Enabled := False; end; destructor TMaschine.Destroy; begin PollTimer.Enabled := False; Active := False; inherited; end; procedure TMaschine.SetInterval(Value: Cardinal); begin FInterval := Value; PollTimer.Interval := Value; end; procedure TMaschine.OnPollTimer(Sender: TObject); begin ReadOpcData; end; procedure TMaschine.SetHostIp(Value: WideString); begin FHostIp := Value; end; procedure TMaschine.SetActive(Value: Boolean); begin FActive := Value; PollTimer.Enabled := Value; end; procedure TMaschine.ReadOpcData; var FirstTickCount: DWord; begin if FActive then begin PollTimer.Enabled := False; OnOpcTaskStart(Self); FOpcTask := TTask.Create(procedure begin {*** HIER WERDEN DIE OPC-DATEN DANN GELESEN DIES IST NUR EINE DUMMYROUTINE UM ZEIT ZU VERBRAUCHEN ***} FirstTickCount := GetTickCount; while ((GetTickCount - FirstTickCount) < 1000) do begin Application.ProcessMessages; if Application.Terminated then Exit; end; {****************************************************************************************************} end); FOpcTask.Start; OnOpcTaskReady(Self); OnOpcData(Self, FMaschinendaten); PollTimer.Enabled := True; end; end; procedure TMaschine.OnOpcTaskStart(Sender: TObject); begin if Assigned(FOnTaskStart) then FOnTaskStart(Self); end; procedure TMaschine.OnOpcTaskReady(Sender: TObject); begin if Assigned(FOnTaskReady) then FOnTaskReady(Self); end; procedure TMaschine.OnOpcData(Sender: TObject; Value: TMaschinendaten); begin if Assigned(FOndata) then FOnData(Self, FMaschinendaten); end; (**********************************************************************************************************************************************************************************) end.
Code:
procedure TfrmMain.FormShow(Sender: TObject);
var Loop: Integer; begin SetLength(Maschinen, 10); // <- z.B. 10 Maschinen... SetLength(Maschinendaten, 10); for Loop := Low(Maschinen) to High(Maschinen) do begin Maschinen[Loop] := TMaschine.Create(Self); try Maschinen[Loop].MaschinenID := Loop + 1; Maschinen[Loop].OnData := OnOpcData; Maschinen[Loop].OnTaskStart := OnTaskStart; Maschinen[Loop].OnTaskReady := OnTaskReady; Maschinen[Loop].PollInterval := 1000; Maschinen[Loop].HostIP := slMaschinenIp.Strings[Loop]; Maschinen[Loop].MaschinenName := slMaschinenName.Strings[Loop]; Maschinen[Loop].Active := True; except // TODO end; end; end; procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin var Loop: Integer; begin if High(Maschinen) > -1 then begin for Loop := Low(Maschinen) to High(Maschinen) do begin if Assigned(Maschinen[Loop]) then begin Maschinen[Loop].Active := False; FreeAndNil(Maschinen[Loop]); end; end; end; CanClose := True; end; procedure TfrmMain.OnOpcData(Sender: TObject; Value: TMaschinendaten); begin MaschinenDaten[(Sender as TMaschine).MaschinenID] := Value; end; procedure TfrmMAin.OnTaskStart(Sender: TObject); begin // TODO end; procedure TfrmMain.OnTaskReady(Sender: TObject); begin // TODO end; |
AW: TTask Frage
Sehe da weder Threads noch Tasks, nur Timer.
Du erstellst pro TMaschinen-Instanz einen normalen Timer mit 1 Sekunde Intervall. Bitte poste deinen Quelltext-Versionsstand, mit dem du wirklich Probleme hast. |
AW: TTask Frage
Hallo TiGü,
danke für die Anmerkung. Hatte den falschen Code genommen... :roll: Ist jetzt aber korrigiert... :-D |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:26 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz