Delphi-PRAXiS
Seite 3 von 5     123 45      

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)

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!


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:41 Uhr.
Seite 3 von 5     123 45      

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