Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Synchronize mit Argumenten (https://www.delphipraxis.net/158203-synchronize-mit-argumenten.html)

Viktorii 9. Feb 2011 13:22

Delphi-Version: 5

Synchronize mit Argumenten
 
Moin.

Erstmal habe ich schon gelesen, dass das was ich im Titel geschrieben habe nicht zu gehen scheint.

Mal ein Beispiel für mein Problem: Ich habe in einem Thread in dem z.B. irgendwelche Fehler auftreten können. Jetzt möchte ich meinem Hauptthread je nach aufgetretenem Fehler verschiedene Strings über geben.

Soweit bin ich schon:

Delphi-Quellcode:
type
  TInstError = procedure(const Content: String) of object;
 
  TMyOwnThread = class(TThread)
  private
    FInstError: TInstError;
    procedure SyncError;
  public
    property InstError: TInstError read FInstError write FInstError;
  end;
 
implementation
 
procedure TMyOwnThread.SyncError;
begin
  if Assigned(FInstError)
    then FInstError('ErrorString');
end;
 
procedure TMyOwnThread.Execute;
begin
  ...
  // Error 1
  Synchronize(SyncError);
  ...
  // Error 2
  Synchronize(SyncError);
  ...
  // Error 3
  Synchronize(SyncError);
 
  usw.
end;


procedure Form1.InstError(const Content: String);
begin
  RichEditError.Lines.Add(Content);
end;

So müsste ich aber für jeden Fehler einen eigene Methode erstellen welche ich mit Synchronize aufrufe.

Ich finde es schon lästig, dass ich überhaupt eine zusätzlich Methode dafür brauche. Das fördert nicht gerade die Übersichtlichkeit.

Aber wenn ich für jeden String ne eigene Methode brauch werd' ich ja bekloppt :pale:

Wie kann ich das eleganter lösen?

Vielen Dank

DeddyH 9. Feb 2011 13:27

AW: Synchronize mit Argumenten
 
Spendier doch dem Thread ein privates Stringfeld, in das Du die Fehlermeldung schreibst. Dieses übergibst Du dann mittels FInstError.

[edit] *Hehe* schneller :) [/edit]

Bummi 9. Feb 2011 13:27

AW: Synchronize mit Argumenten
 
Delphi-Quellcode:
procedure TMyOwnThread.SyncError;
begin
  if Assigned(FInstError)
    then FInstError(FError);
end;
 
procedure TMyOwnThread.Execute;
begin
  ...
  // Error 1
  FError := 'Error 1' ;
  Synchronize;
  ...
  // Error 2
  FError := 'Error 2' ;
  Synchronize;
 
  usw.
end;

Sir Rufo 9. Feb 2011 13:37

AW: Synchronize mit Argumenten
 
Tja, das ist mit der Delphi-Version blöde ... bei den neueren Versionen macht man das ganz charmant über Anonyme Methoden.

Aber so sollte es auch charmant gehen:

Delphi-Quellcode:
type
  TInstError = procedure(const Content: String) of object;
 
  TMyOwnThread = class(TThread)
  private
    FInstError: TInstError;
    // procedure SyncError; @DeddyH: logisch muss die hier raus :o)
    CallError( const Content : string );
  public
    property InstError: TInstError read FInstError write FInstError;
  end;
 
implementation

procedure TMyOwnThread.CallError( const Content : string );
procedure SyncError;
begin
  if Assigned( FInstError )
    then FInstError( Content );
end;
begin
  Synchronize( SyncError );
end;
 
procedure TMyOwnThread.Execute;
begin
  ...
  // Error 1
  CallError( 'Fehler 1' );
  ...
  // Error 2
  CallError( 'Fehler 2' );
  ...
  // Error 3
  CallError( 'Fehler 3' );
 
  usw.
end;


procedure Form1.InstError(const Content: String);
begin
  RichEditError.Lines.Add(Content);
end;

wicht 9. Feb 2011 13:40

AW: Synchronize mit Argumenten
 
Sir Rufo, das ist super!
Ich habe nie daran gedacht, das mit 'Nested'-Methoden zu machen, falls das so heißt. Habe immer 2 direkt in die Klasse gebaut und das fand ich auch immer sehr unschön.

Dankeschön :)

DeddyH 9. Feb 2011 13:41

AW: Synchronize mit Argumenten
 
Müsste die Deklaration von SyncError dann aber nicht aus dem private-Abschnitt verschwinden?

Sir Rufo 9. Feb 2011 13:48

AW: Synchronize mit Argumenten
 
Zitat:

Zitat von wicht (Beitrag 1080524)
Sir Rufo, das ist super!
Ich habe nie daran gedacht, das mit 'Nested'-Methoden zu machen, falls das so heißt. Habe immer 2 direkt in die Klasse gebaut und das fand ich auch immer sehr unschön.

Dankeschön :)

Aber du hast doch mit XE die Möglichkeit der Anonymen Methoden und da gibt es einen sehr charmanten Ansatz von Uwe Raabe

Ich würde aber so gut wie immer statt
Delphi-Quellcode:
Synchronize
den
Delphi-Quellcode:
Queue
verwenden (manchmal muss halt Sync sein, aber beim reinen Abliefern von Informationen ...).
Nur ganz zum Schluss mindestens einmal noch
Delphi-Quellcode:
Synchronize
, damit auch wirklich alles übertragen wird.

Der Vorteil bei
Delphi-Quellcode:
Queue
ist ja, dass zwar synchronisiert wird, aber ohne den laufenden Prozess zu unterbrechen. Bei
Delphi-Quellcode:
Synchronize
wartet beide Threads aufeinander, bis diese synchron laufen, verarbeiten die Informationen und dann geht es für beide erst wieder weiter.

Und warum soll de Thread darauf warten, bis da was auch immer auf den Bildschirm geklatscht werden kann.
Das Fenster ist doch schon groß und kann das ganz alleine :mrgreen:

Zitat:

Zitat von DeddyH (Beitrag 1080525)
Müsste die Deklaration von SyncError dann aber nicht aus dem private-Abschnitt verschwinden?

:oops: jooo ... habs berichtigt

wicht 9. Feb 2011 14:08

AW: Synchronize mit Argumenten
 
Zitat:

Aber du hast doch mit XE die Möglichkeit der Anonymen Methoden und da gibt es einen sehr charmanten Ansatz von Uwe Raabe
Aha, ich war zu voreilig. Habe mir XE gestern bestellt, aber musste mein Profil vorher schon anpassen :-D .. Aber danke für diese Information, werde mich, wenn die Bestellung durch ist, auf jedenfall mal mit den anonymen Methoden auseinandersetzen.

Nochmal Danke und LG

himitsu 9. Feb 2011 15:29

AW: Synchronize mit Argumenten
 
oder Hier im Forum suchenThreadHelper (mit anonymen Methoden ab D2009), bzw. Hier im Forum suchenAsyncCalls

Sir Rufo 9. Feb 2011 21:18

AW: Synchronize mit Argumenten
 
Verflixte Technik ... geht doch nicht, da "Symbol SyncError kann nicht erfasst werden"

Na dann am besten auf XE umsteigen :mrgreen:
Zitat:

Zitat von Sir Rufo (Beitrag 1080523)
Tja, das ist mit der Delphi-Version blöde ... bei den neueren Versionen macht man das ganz charmant über Anonyme Methoden.

Aber so sollte es auch charmant gehen:

Delphi-Quellcode:
type
  TInstError = procedure(const Content: String) of object;
 
  TMyOwnThread = class(TThread)
  private
    FInstError: TInstError;
    // procedure SyncError; @DeddyH: logisch muss die hier raus :o)
    CallError( const Content : string );
  public
    property InstError: TInstError read FInstError write FInstError;
  end;
 
implementation

procedure TMyOwnThread.CallError( const Content : string );
procedure SyncError;
begin
  if Assigned( FInstError )
    then FInstError( Content );
end;
begin
  Synchronize( SyncError );
end;
 
procedure TMyOwnThread.Execute;
begin
  ...
  // Error 1
  CallError( 'Fehler 1' );
  ...
  // Error 2
  CallError( 'Fehler 2' );
  ...
  // Error 3
  CallError( 'Fehler 3' );
 
  usw.
end;


procedure Form1.InstError(const Content: String);
begin
  RichEditError.Lines.Add(Content);
end;


Viktorii 10. Feb 2011 14:32

AW: Synchronize mit Argumenten
 
Erstmal vielen Dank an alle für die Antworten.

Ich habe den Unterschied zwischen Syncronize und Queue nicht so wirklich verstanden. Und die Hilfe hilft mir beim Verständnis auch nicht wirklich weiter.

Kann mir das jemand mal erläutern wann ich Queue verwenden kann und wann ich es nicht darf?

DeddyH 10. Feb 2011 14:42

AW: Synchronize mit Argumenten
 
Soweit ich es selbst verstanden habe, verhält es sich ähnlich wie bei SendMessage(Synchronize) und PostMessage(Queue): bei Synchronize wartet der Thread, bis der HauptThread die angegebene Methode abgearbeitet hat, bei Queue wird dem HauptThread einfach die Methode "aufs Auge gedrückt", und der Thread läuft weiter. Somit kann bei Verwendung von Queue die Gefahr bestehen, dass der HauptThread nur noch damit beschäftigt ist, die aufgelaufenen Methodenaufrufe abzuarbeiten, nämlich wenn er länger für die Abarbeitung braucht als der Thread. Aber das sind jetzt keine gesicherten Angaben, sondern nur meine Interpretation. Sollte ich mich irren, bitte ich einen Wissenden um Korrektur.

Sir Rufo 10. Feb 2011 14:59

AW: Synchronize mit Argumenten
 
@DeddyH: genau so ist es und genau so steht es in der Hilfe

Zitat:

Zitat von Queue
Führt einen Methodenaufruf im Haupt-Thread asynchron aus.

Zitat:

Zitat von Synchronize
Führt einen Methodenaufruf im Haupt-Thread aus.


Viktorii 10. Feb 2011 15:08

AW: Synchronize mit Argumenten
 
Hmm... Okay. Wenn das denn nun wirklich so ist :-) Wie ist dass dann bei dem Lösungsansatz von DeddyH und Bummi:

Delphi-Quellcode:
procedure TMyOwnThread.SyncError;
begin
  if Assigned(FInstError)
    then FInstError(FError);
end;
 
procedure TMyOwnThread.Execute;
begin
  ...
  // Error 1
  FError := 'Error 1' ;
  Queue(SyncError);
  ...
  // Error 2
  FError := 'Error 2' ;
  Queue(SyncError);
 
  usw.
end;
Wenn Error 1 und Error 2 schnell hintereinander ausgeführt werden und der Hauptthread langsam ist, dann würde FError ja evtl. schon verändert worden sein bevor der Hauptthread FError unter Error 1 ausgelesen hat!?!

Also das typische Problem bei zwei Threads und einer Resource. Also könnte man Critical Sections verwenden. Aber dann wäre man wieder blockieren :drunken:

Da beißt sich die Schlange wieder selbst in den Schwanz :-D

DeddyH 10. Feb 2011 15:18

AW: Synchronize mit Argumenten
 
Ob FError in der Zwischenzeit den Wert geändert hat, dürfte keine Rolle spielen, da die Methode ja bereits mit dem entsprechenden Parameter in die Abarbeitungsschleife des MainThreads gestellt wurde.

Viktorii 10. Feb 2011 15:58

AW: Synchronize mit Argumenten
 
Leider funktioniert es nicht.

Habe mal folgenden Versuch gemacht:

Delphi-Quellcode:
type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FMyThread: TMyThread;
    procedure progress(value: Integer);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  FMyThread := TMyThread.Create(true);
  FMyThread.Progress := progress;
  FMyThread.Resume;
end;

procedure TForm1.progress(value: Integer);
begin
  Memo1.Lines.Add('Value: ' + IntToStr(value));
  sleep(500);
end;
Delphi-Quellcode:
type
  TProgress = procedure(Value: integer) of object;

type
  TMyThread = class(TThread)
  private
    FValue: Integer;
    FProgress: TProgress;
    procedure SyncProgress;
  protected
    procedure Execute; override;
  public
    property Progress: TProgress write FProgress;

  end;

implementation

uses
  Windows, SysUtils;
{ TMyThread }

procedure TMyThread.Execute;
var
  i: Integer;
begin
  for i := 1 to 10 do
  begin
    FValue := i;
    Queue(SyncProgress);
    sleep(200);
    OutputDebugString(PWideChar('FValue: ' + IntToStr(FValue)));
  end;
end;

procedure TMyThread.SyncProgress;
begin
  FProgress(FValue);
end;
Die Ausgabe sieht so aus:
Zitat:

Value: 1
Value: 3
Value: 5
Value: 8
Value: 10
Value: 10
Value: 10
Value: 10
Value: 10
Value: 10
:(

DeddyH 10. Feb 2011 15:59

AW: Synchronize mit Argumenten
 
Und bei Synchronize stimmt das Ergebnis?

Sir Rufo 10. Feb 2011 18:00

AW: Synchronize mit Argumenten
 
Na logisch stimmt das bei Synchronize.

In die Warteschlange kommt ja der Aufruf der Prozedur.
beim Abarbeiten wird die Prozedur ausgeführt.

Und bei der Verwendung von Queue (asynchron) hat sich der Wert von FValue eben schon wieder geändert.
Bei Synchronize schweigt ja alles, bis die Prozedur abgearbeitet wurde.

Für das Queue hilft also auch nur einen WerteQueue (mit CS absichern) die dann Stück für Stück abgearbeitet wird.

Nimm für die WerteQueue eine eigene CS, damit die auch nur dann blockiert ist, wenn dort Werte abgerufen oder eingetragen werden.

Bummi 10. Feb 2011 21:16

AW: Synchronize mit Argumenten
 
@Sir Rufo
Der Zugriff über CS ist sicher sinnvoll, ändert aber nichts daran dass man sich im klaren sein muss wo der Unterschied zwischen Synchronize und Queue liegt und welchen Tod man sterben möchte.
Es wird Anwendungen geben bei es beispielsweise genügt wenn der Hauptthread einen gerade aktuellen Wert zu Anzeige bringt, hier wird Queue der richtige Ansatz sein. Wenn sichergestellt werden muss dass alle Messwerte auflaufen wird es auf Synchonize oder auf z.B. gemeinsam genutzte Array hinauslaufen, bei denen der Thread eine CS gesicherte "gültig bis" Property verwaltet, oder bei einfachen Datentypen direkt Messages.

Sir Rufo 10. Feb 2011 21:54

AW: Synchronize mit Argumenten
 
@Bummi

Wenn man mit Queue arbeitet MUSS man den Zugriff auf die entsprechende Ressource (hier FValue) schützen. Andernfalls riskiert man einen Zugriffsfehler.

Bei Synchronize kann man sich das sparen, da zum Zeitpunkt der Übergabe Thread und Hauptthread im gleichen Threadkontext laufen. Bei Queue eben halt nicht.

Mit einer MessageQueue werden via Queue auch ALLE Nachrichten abgeliefert. Mit nur einer Resoource (hier FValue) natürlich nur der aktuelle Wert zum Zeitpunkt der Abfrage.

Hat man sich also entschlossen mit Queue zu arbeiten (weil man eben nur Daten in den Hauptthread schleusen will und von dem auch nichts direkt zurückhaben will und genau da macht es durchaus Sinn), dann ist eine kleine Liste mit den entsprechenden Nachrichten doch auch kein Problem mehr.

Nur sollte man dabei genau wissen, was man tut, sonst knallt es völlig unkontrolliert (und perverserweise auch nicht regelmäßig und je nach Häufigkeit des Zugriffs auch nicht wirklich oft ... aber es knallt halt :)

Bummi 10. Feb 2011 23:05

AW: Synchronize mit Argumenten
 
@Sir Rufo

ich war schon vorher Deiner Meinung, meine Thread-Templates sind auch so aufgebaut.
Mir ging es nur darum festzustellen dass es wohl jedes mal abzuwägen gilt welche der Varianten (Queue/Synchronize) für den Anwendungsfall gerade sinnvoll ist.
Zitat:

Ich würde aber so gut wie immer statt Synchronize den Queue verwenden (manchmal muss halt Sync sein, aber beim reinen Abliefern von Informationen ...).
Es gibt IMHO auch Anwendungsfälle in denen z.B. kein Messwert verloren gehen darf (also durch einen späteren ersetzt werden).

BTW leicht offtopic, wie handhabst Du Syncs zwischen Threads und SubThreads (oder nebenläufigen Threads), also ohne Umweg über den Anwendungsthread?

Sir Rufo 11. Feb 2011 00:29

AW: Synchronize mit Argumenten
 
Wieso gehen beim Queue Werte verloren? Klar, wenn man es falsch macht ja, aber wenn ich das Auto nicht richtig beherrsche fahre ich evtl. auch gegen den Baum. :mrgreen:
Also am besten richtig machen ;)

Bei meinen Queues gehen keine Werte verloren.
(und seit der anonymen Methoden geht halt auch das Queue ohne eigene Warteschlange)

Wichtig ist nur am Ende des Threads ein Synchronize zu machen, damit auch wirklich alle Queue-Einträge im Hauptthread verarbeitet wurden. Ansonsten kann es eben halt passieren, dass der Thread (FreeOnTerminate) sich einfach so auflöst und alle bis dahin nicht abgearbeitete Queue-Einträge aus selbigen Thread gehen sang und klanglos mit unter.

Zwischen den Threads (auch Hauptthread direkt zum Thread) geht das bei mir immer über eine CS.
Manchmal halt auch eine separate CS für den reinen Austausch dieser Daten, wenn es vom Ablauf geschickter ist und dadurch performanter wird.

ADD Daten von Thread zu Thread über den Hauptthread auszutauschen halte ich nicht für sinnvoll.
Der Hauptthread ist für den User und wird von dem quasi kontrolliert. Wenn der also Samba auf der Tastatur und Maus tanzt, dann könnte er damit auch die Threads beeinflussen.
Bei mir darf der das (wenn es nicht meine Maus/Tastatur ist) ;)

himitsu 11. Feb 2011 01:00

AW: Synchronize mit Argumenten
 
Wenn man zusammen mit dem Prozedurzeiger auch noch die Parameter mit in dem Queue ablegt, dann gäbt es diesbezüglich auch keine Probleme.

Bzw., man eh nur die selbe Funktion aufrugt, dann reichen auch nur die Werte im Queue, wenn nichts verlorengehen soll.

Bummi 11. Feb 2011 07:42

AW: Synchronize mit Argumenten
 
Zitat:

Bei meinen Queues gehen keine Werte verloren.
(und seit der anonymen Methoden geht halt auch das Queue ohne eigene Warteschlange)
dann haben wir eine Kommunikationsproblem, oder ich einen Hänger, wie löst Du #16 mit Queue so dass Du alle erwarteten Werte zurückbekommst?

Viktorii 11. Feb 2011 09:36

AW: Synchronize mit Argumenten
 
Ich steh immer noch auf dem Schlauch. Habe den Zugriff mit eine CS geschützt.

In dieser CS übertrage ich meine Daten an eine TQueue (Das erste mal dass ich mit Generics gearbeitet habe :stupid:) damit die Blockierung auf ein zeitliches Minimum begrenzt bleibt. Allerdings blockiert das Abarbeiten der Queue die Funktion und es gehen immer noch Werte 'verloren'.
Delphi-Quellcode:
type
  TValueQueue = TQueue<Integer>;

  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FMyThread: TMyThread;
    FValueQueue: TValueQueue;
    procedure progress(value: Integer);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  FValueQueue := TValueQueue.Create;
  FMyThread := TMyThread.Create(true);
  FMyThread.Progress := progress;
  FMyThread.Resume;
end;

procedure TForm1.progress(value: Integer);
begin
  FMyThread.SyncCS.Enter;
  FValueQueue.Enqueue(value);
  FMyThread.SyncCS.Leave;

  while FValueQueue.Count > 0 do
  begin
    Memo1.Lines.Add('Value: ' + IntToStr(FValueQueue.Dequeue));
    sleep(500);
  end;
end;
Delphi-Quellcode:
type
  TProgress = procedure(Value: integer) of object;

type
  TMyThread = class(TThread)
  private
    FValue: Integer;
    FProgress: TProgress;
    FSyncCS: TCriticalSection;
    procedure SyncProgress;
  protected
    procedure Execute; override;
  public
    property Progress: TProgress write FProgress;
    property SyncCS: TCriticalSection read FSyncCS;

  end;

implementation

uses
  Windows, SysUtils;
{ TMyThread }

procedure TMyThread.Execute;
var
  i: Integer;
begin
  FSyncCS := TCriticalSection.Create;
  for i := 1 to 10 do
  begin
    FSyncCS.Enter;
    FValue := i;
    FSyncCS.Leave;

    Queue(SyncProgress);
    sleep(200);
  end;
  //if assigned(FSyncCS) then
  //  FreeAndNil(FSyncCS);
end;

procedure TMyThread.SyncProgress;
begin
  FProgress(FValue);
end;
Wie mache ich es richtig(er)?

Viktorii 11. Feb 2011 10:28

AW: Synchronize mit Argumenten
 
!!! OFF TOPIC !!!

Ist ja echt frustrierend :evil: RAD heißt doch Rapid Application Development. Und es heißt doch immer mit Delphi geht alles immer so einfach im Gegensatz zu cpp. Ich habe ein Embedded Projekt mit Qt geschrieben und dachte mir ich versuche jetzt mal ob ich bei Qt auch dieses Problem habe....

Und siehe da, alles ganz einfach. Keine zusätzlichen privaten Felder in denen die zu übergebenden Werte stehen, keine zusätzlichen CS, keine zusätzlichen Methoden, keine Generic Queue oder was weiß ich für Tricksereien und im Gegensatz zu meiner Lösung bei Delphi funktioniert diese Qt Lösung (auf Anhieb nach 10 min) :-(

Code:
class MainWindow : public QMainWindow
{
    Q_OBJECT

   public:
      explicit MainWindow(QWidget *parent = 0);
      ~MainWindow();

   private slots:
      void on_pushButton_clicked();
      void onProgress(qint32);

   private:
      Ui::MainWindow *ui;
};


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
   MyThread *myThread;

   myThread = new MyThread(this);

   connect(myThread,   SIGNAL(progress(qint32)),
         this,      SLOT(onProgress(qint32)));

   myThread->start();
}

void MainWindow::onProgress(qint32 value)
{
   ui->plainTextEdit->appendHtml("Value: " + QString::number(value));
   Sleep(500);
   qDebug() << QThread::currentThreadId() << "MainWindow::onProgress - Value:" << QString::number(value);
}
Code:
class MyThread : public QThread
{
    Q_OBJECT
   public:
      explicit MyThread(QObject *parent = 0);

   signals:
      void progress(qint32 value);

   public slots:

   protected:
      void run();

};


#include "MyThread.h"
#include <windows.h>
#include <QDebug>

MyThread::MyThread(QObject *parent) :
    QThread(parent)
{
}

void MyThread::run()
{
   for(qint32 i = 1; i <= 10; i++)
   {
      emit progress(i);
      Sleep(200);
      qDebug() << QThread::currentThreadId() << "MyThread::run() - Value:" << QString::number(i);
   }
   qDebug() << QThread::currentThreadId() << "Fertig";
}

Zitat:

0x14e8 MyThread::run() - Value: "1"
0x14e8 MyThread::run() - Value: "2"
0x13b4 MainWindow::onProgress - Value: "1"
0x14e8 MyThread::run() - Value: "3"
0x14e8 MyThread::run() - Value: "4"
0x13b4 MainWindow::onProgress - Value: "2"
0x14e8 MyThread::run() - Value: "5"
0x14e8 MyThread::run() - Value: "6"
0x14e8 MyThread::run() - Value: "7"
0x13b4 MainWindow::onProgress - Value: "3"
0x14e8 MyThread::run() - Value: "8"
0x14e8 MyThread::run() - Value: "9"
0x14e8 MyThread::run() - Value: "10"
0x14e8 Fertig
0x13b4 MainWindow::onProgress - Value: "4"
0x13b4 MainWindow::onProgress - Value: "5"
0x13b4 MainWindow::onProgress - Value: "6"
0x13b4 MainWindow::onProgress - Value: "7"
0x13b4 MainWindow::onProgress - Value: "8"
0x13b4 MainWindow::onProgress - Value: "9"
0x13b4 MainWindow::onProgress - Value: "10"
So, das musste mal raus. Jetzt gehts mir besser :stupid:

Und wie mache ich es jetzt richtig in Delphi (siehe #25)?

himitsu 11. Feb 2011 11:23

AW: Synchronize mit Argumenten
 
Du übergibst da aber den Wert direkt an den Thread, bzw du gibst jedem Thread eine Kopie, so daß alle Threads ihre eigenen Werte haben.

In diesen Delphibeispielen nutzt du eine externe Variable für alle Threads.

> Äpfel und Birnen

Bummi 11. Feb 2011 11:27

AW: Synchronize mit Argumenten
 
@himitsu
und wie übergebe ich Delphi eine Kopie per Queue?

Uwe Raabe 11. Feb 2011 12:10

AW: Synchronize mit Argumenten
 
Sir Rufo hat ja bereits auf den Artikel in meinem Blog verwiesen, der den Aufruf von Synchronize mit einer Anonymen Methode zeigt. Natürlich kann man dieses Verfahren 1:1 auch auf Queue anwenden (habe ich auch erwähnt).

Allerdings muss man dann aufpassen (habe ich am Ende des Artikels auch drauf hingewiesen), daß der Thread sich nicht bereits beendet bevor die Queues alle abgearbeitet wurden. Ansonsten gehen die letzten Einträge nämlich verloren (gilt aber für alle Implementationen, die Queue benutzen).

Der Vorteil der Anonymen Methode ist einfach, daß man sich um die Kapselung des Parameters keine Gedanken mehr machen muss - das erledigt der Compiler. Das ganze Brimborium mit der TValueQueue ist damit völlig überflüssig.

Ach ja: Sollte jemand Probleme haben, den Artikel zu vestehen, da er auf Englisch verfasst ist, könnte ich mich vielleicht dazu durchringen, eine Deutsche Fassung hier an geeigneter Stelle (wo?) bereitzustellen.

Viktorii 11. Feb 2011 13:39

AW: Synchronize mit Argumenten
 
Also ich bekomme es mit queue nicht hin. :(

Wie muss mein Code von #16 oder #26 abgeändert werden damit das funktioniert?

Könnte den korrekten code jemand evtl. posten? Wäre sehr dankbar!

Bummi 11. Feb 2011 14:05

AW: Synchronize mit Argumenten
 
Danke Uwe, ich hoffe ich habe es richtig verstanden:
Delphi-Quellcode:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;



type
  TProgress = procedure(const Value: integer) of object;

type
  TMyThread = class(TThread)
  private
    FValue: Integer;
    FProgress: TProgress;
    procedure SyncProgress(Value:Integer);
  protected
    procedure Execute; override;
  public
    property Progress: TProgress write FProgress;

  end;
  TForm2 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    FMyThread: TMyThread;
    procedure progress(const value: Integer);
  public
    { Public-Deklarationen }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TMyThread.Execute;
var
  i: Integer;
begin
  for i := 1 to 10 do
  begin
    FValue := i;
    SyncProgress(i);
    sleep(200);
    OutputDebugString(PWideChar('FValue: ' + IntToStr(FValue)));
  end;
end;

procedure TMyThread.SyncProgress(Value:Integer);
begin
    Queue(
    Procedure
      begin
          FProgress(Value);
      end
    );
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  FMyThread := TMyThread.Create(true);
  FMyThread.Progress := progress;
  FMyThread.Resume;
end;

procedure TForm2.progress(const value: Integer);
begin
  Memo1.Lines.Add('Value: ' + IntToStr(value));
  sleep(500);
end;
end.
Korrekturvorschläge willkommen....

Viktorii 11. Feb 2011 14:13

AW: Synchronize mit Argumenten
 
Also auch mit den Änderungen sieht die Ausgabe bei mir so aus:
Zitat:

Value: 1
Value: 3
Value: 5
Value: 8
Value: 10
Value: 10
Value: 10
Value: 10
Value: 10
Value: 10

Bummi 11. Feb 2011 14:16

AW: Synchronize mit Argumenten
 
bei mir so, XE Pro
Code:
Value: 1
Value: 2
Value: 3
Value: 4
Value: 5
Value: 6
Value: 7
Value: 8
Value: 9
Value: 10
mhhhhhhh.....:gruebel:

vielleicht können Uwe oder Sir Rufo uns nochmals auf die Sprünge helfen.

Uwe Raabe 11. Feb 2011 16:05

AW: Synchronize mit Argumenten
 
Der Code von Bummi in #31 ist soweit in Ordnung, bis auf ein paar Kleinigkeiten:

a) das Feld FValue kann entfallen, da es nur noch innerhalb Execute verwendet wird

b) du musst noch etwas tun, damit der Thread nicht beendet wird, bevor die Queues abgearbeitet sind. Das genaue Verhalten ist natürlich von diversen Umständen abhängig, aber bei meinem System fehlt die 10 im Memo, wenn ich das OutputDebugString auskommentiere und es kommt fast gar nichts mehr an, wenn ich das Sleep(200) auch weglasse.

Und Viktorii: Wenn es bei dir nicht geht, hast du vermutlich noch einen Fehler drin. Kann ich ohne aktuelle Sourcen aber nicht erkennen.

Sir Rufo 11. Feb 2011 16:14

AW: Synchronize mit Argumenten
 
Zitat:

Zitat von Viktorii (Beitrag 1081013)
(Das erste mal dass ich mit Generics gearbeitet habe :stupid:)

:wiejetzt:

Seit wann kann Delphi 5 mit Generics umgehen?

Uwe Raabe 11. Feb 2011 16:22

AW: Synchronize mit Argumenten
 
Zitat:

Zitat von Sir Rufo (Beitrag 1081089)
Zitat:

Zitat von Viktorii (Beitrag 1081013)
(Das erste mal dass ich mit Generics gearbeitet habe :stupid:)

:wiejetzt:

Seit wann kann Delphi 5 mit Generics umgehen?

Und TThread.Queue gab's damals auch noch nicht...

Viktorii 11. Feb 2011 17:49

AW: Synchronize mit Argumenten
 
Also wie das Delphi 5 dahinkommt kann ich mir auch nicht erklären :shock:

Hab jetzt mittlerweile auch Delphi XE. Sorry für die Verwirrung.


Was den Code angeht: Kann sein dass ich da einen Bug eingebaut habe. War ein wenig zwischen Tür und Angel getestet.

Wie es aussieht habe ich vor Montag kein Delphi zur Verfügung. Werde es spätestens dann testen.

Schönes Wochenende an alle,

Bummi 11. Feb 2011 18:29

AW: Synchronize mit Argumenten
 
nochmals Danke an Uwe und Sir Rufo

Sir Rufo 11. Feb 2011 18:59

AW: Synchronize mit Argumenten
 
Liste der Anhänge anzeigen (Anzahl: 2)
Sodele, jetzt habe ich mal einen DemoThread inkl. einer Exe zum Ausprobieren.

Mit der Exe kann man schön das Zeitverhalten von Queue und Sync beobachten (auch, dass keine Werte verloren gehen).
Für den Vergleich unter Last gibt es unten den Schalter "Arbeitstimer aktiv". Dieser simuliert eine starke Auslastung vom MainThread.

Interessant ist dabei das Verhalten von Queue und Sync zu sehen.

Den Code-Vorschlag von Uwe habe ich mal eingearbeitet (als SlowSync) und eine kleine Abwandlung (FastSync).
Das Fast und SLow kommt allerdings erst dann zum Tragen, wenn das Event für den Sync nicht zugewiesen ist,
also eigentlich kein Sync erfolgen müsste.

Der Code von Uwe führt aber immer den Sync aus => SlowSync
Mein Code führt den Sync nur aus wenn auch wirklich benötigt => FastSync

Aber ... der Zugriff auf die Prop OnInfoEvent erfolgt beim FastSync noch im ThreadKontext und muss daher entsprechend geschützt sein (siehe Getter von OnInfoEvent).

Der SlowSync kann direkt auf des Feld FOnInfoEvent zugreifen (es sei denn diese Prop würde auch von anderen Thread gelesen/beschrieben, dann muss das eh komplett mit einer CS gesichert werden)

Hier der ThreadCode (Exe und kompletter Source im Anhang):
Delphi-Quellcode:
unit uDemoThread;

interface

uses
  Classes,
  SyncObjs;

type
  TNotifyInfoEvent = procedure( const Info : string; Ticks : Cardinal ) of object;

  TThreadExecType = ( tetSlowSync, tetFastSync, tetQueue );

  TDemoThread = class( TThread )
  private
    { Private-Deklarationen }
    FEventCS :    TCriticalSection;
    FOnInfoEvent : TNotifyInfoEvent;
    FExecType :   TThreadExecType;
    procedure CallSlowSyncEvent( const Info : string; Ticks : Cardinal = 0 );
    procedure CallFastSyncEvent( const Info : string; Ticks : Cardinal = 0 );
    procedure CallQueueEvent( const Info : string; Ticks : Cardinal = 0 );
    function GetOnInfoEvent : TNotifyInfoEvent;
    procedure SetOnInfoEvent( const Value : TNotifyInfoEvent );
  protected
    procedure Execute; override;
  public
    constructor Create( CreateSuspended : Boolean; ExecType : TThreadExecType = tetSlowSync );
    destructor Destroy; override;
  published
    property OnInfoEvent : TNotifyInfoEvent
      read  GetOnInfoEvent
      write SetOnInfoEvent;
  end;

implementation

uses
  SysUtils, Windows;

{ TDemoThread }

procedure TDemoThread.CallSlowSyncEvent( const Info : string; Ticks : Cardinal );
var
  IntTicks : Cardinal;
begin
  // Der SlowSync synchronisiert IMMER mit dem Hauptthread
  // egal, ob ein Event verknüpft ist oder nicht
  // Wenn kein Event verknüpft ist, bräuchte ja eigentlich kein Sync erfolgen
  if MainThreadID = GetCurrentThreadId
  then
    begin
      if Assigned( FOnInfoEvent )
      then
        FOnInfoEvent( Info, Ticks );
    end
  else
    begin
      IntTicks := GetTickCount;
      Synchronize( procedure begin CallSlowSyncEvent( 'via SlowSync: ' + Info, IntTicks ) end );
    end;
end;

procedure TDemoThread.CallFastSyncEvent( const Info : string; Ticks : Cardinal );
var
  IntTicks : Cardinal;
begin
  // Der Fast-Sync synchronisiert nur, wenn auch ein Event zugewiesen wurde
  // Da diese Abfrage aber noch im Thread-Kontext erfolgt, muss die
  // Eigenschaft OnInfoEvent über eine CS abgesichert werden
  if Assigned( OnInfoEvent )
  then
    if MainThreadID = GetCurrentThreadId
    then
      FOnInfoEvent( Info, Ticks )
    else
      begin
        IntTicks := GetTickCount;
        Synchronize( procedure begin CallFastSyncEvent( 'via FastSync: ' + Info, IntTicks ) end );
      end;
end;

procedure TDemoThread.CallQueueEvent( const Info : string; Ticks : Cardinal );
var
  IntTicks : Cardinal;
begin
  // Beim Queue ist es völlig wurscht, ob da nun eine Sync erfolgt oder nicht,
  // denn dieser Aufruf erfolgt eh asynchron und beschäftigt den Thread nicht weiter
  if MainThreadID = GetCurrentThreadId
  then
    begin
      if Assigned( OnInfoEvent )
      then
        FOnInfoEvent( Info, Ticks );
    end
  else
    begin
      IntTicks := GetTickCount;
      Queue( procedure begin CallQueueEvent( 'via Queue: ' + Info, IntTicks ) end );
    end;
end;

constructor TDemoThread.Create( CreateSuspended : Boolean; ExecType : TThreadExecType );
begin
  FEventCS := TCriticalSection.Create;
  FEventCS.Enter;
  try
    inherited Create( CreateSuspended );
    FExecType      := ExecType;
    FreeOnTerminate := True;
  finally
    FEventCS.Leave;
  end;
end;

destructor TDemoThread.Destroy;
begin
  FEventCS.Enter;
  try

    inherited;
  finally
    FEventCS.Leave;
    FreeAndNil( FEventCS );
  end;
end;

procedure TDemoThread.Execute;
var
  idx : Integer;
  Info : string;
begin
  Sleep( 25 );
  { Thread-Code hier einfügen }
  for idx := 0 to 10 do
    begin
      Info := 'Ich bin bei ' + IntToStr( idx );
      case FExecType of
        tetSlowSync :
          CallSlowSyncEvent( Info );
        tetFastSync :
          CallFastSyncEvent( Info );
        tetQueue :
          CallQueueEvent( Info );
      end;
    end;

  // Wenn dieser "Slow"-Sync nicht gemacht wird, kann es sein,
  // dass einige Nachrichten über die Queue verloren gehen!!!!
  case FExecType of
    tetSlowSync: CallSlowSyncEvent( 'Fettich!' );
    tetFastSync: CallFastSyncEvent( 'Fettich!' );
    tetQueue: CallSlowSyncEvent( 'Fettich! (Queue)' );
  end;

end;

function TDemoThread.GetOnInfoEvent : TNotifyInfoEvent;
begin
  FEventCS.Enter;
  try
    Result := FOnInfoEvent;
  finally
    FEventCS.Leave;
  end;
end;

procedure TDemoThread.SetOnInfoEvent( const Value : TNotifyInfoEvent );
begin
  FEventCS.Enter;
  try
    FOnInfoEvent := Value;
  finally
    FEventCS.Leave;
  end;
end;

end.

Viktorii 14. Feb 2011 12:03

AW: Synchronize mit Argumenten
 
So, der Code funktioniert :bounce1:

Jetzt muss ich nur noch das Beispiel von Sir Rufo komplett nachvollziehen :)

Vielen Dank an Alle :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:35 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz