Autor Beitrag
JoelH
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 806
Erhaltene Danke: 17

Win10
Delphi Alexandria 11.2 Patch 1
BeitragVerfasst: Di 16.06.15 13:45 
Folgendes Problem, ich habe eine Anwendung die viel mit Datenbankenzugriffen arbeitet und deren Anwender ab und an offenbar zu schnell sind. Dadurch kommt es vereinzelt zu Race Conditions und Daten werden fehlerhaft manipuliert. Durch massives Flag setzen bin ich dem Ganzen zwar gut zu Leibe gerückt und konnte das Ganze auf genau eine Function eingrenzen. Aber hier kommt es sporadisch immer mal wieder vor (alle paar Monate). Nun habe ich mir die Frage gestellt ob es eventuell eine Eventqueue gibt die ausstehende bzw. noch nicht beendete Event Application weit bereitstellt. Denn damit wäre es ja ein leichtest so lange zu warten bis keine potenziellen Aufrufe mehr im Raum stehen und erst dann fort zu fahren.

_________________
mfg. Joel
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Di 16.06.15 21:12 
Von so einer Queue weiß ich nichts, aber es muss natürlich irgendwo eine geben. Vermutlich aber innerhalb des Betriebssystems.

Du kannst dir aber deine eigene Queue schreiben. Alle messages kommen in der Methode WndProc an. Die kannst du überschreiben und dann die Messages filtern (z.B.). Ein Beispiel für WndProc findest du unter www.entwickler-ecke....ewtopic.php?t=109237. Ungefähr so sähe das dann wohl aus:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TStoppuhr.WndProc(var msg : TMessage);
begin
  Queue.Push( msg ); //erstmal nichts tun
end;

procedure QueueProcess();
begin
  while not doBlockMessage(Queue.firstMessage) do
    inherited WndProc(msg); //message verarbeiten
end;

function doBlockMessage(msg: TMessage): boolean;
begin
  if msg.msg=WM_LBUTTONDOWNthen
    Result := not isButtonDownHandlerRunning
  else
    Result := true;
end;

_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mi 17.06.15 00:09 
Solange man Application.ProcessMessages vermeidet, sollte das soch eh nicht vorkommen!?
Denn dann wird eine Nachricht ja vollständig abgearbeitet, bevor die nächste rein kommt. Es gibt ja für jeden Thread die Windows-eigene Messagequeue.
Wenn man natürlich Application.ProcessMessages einsetzt, muss man überall Flags setzen und wundert sich über Race Conditions innerhalb eines einzigen Threads ... :roll:

Mein Rat also: Alles was lange dauert anständig in einen Thread auslagern - beim Rest die GUI blockiert lassen :angel:

Notnagel: Eine Funktion, die lediglich Resize- und Paint-Nachrichten verarbeitet, statt Application.ProcessMessages einsetzen.
JoelH Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 806
Erhaltene Danke: 17

Win10
Delphi Alexandria 11.2 Patch 1
BeitragVerfasst: Mi 17.06.15 07:30 
user profile iconjfheins hat folgendes geschrieben Zum zitierten Posting springen:
Solange man Application.ProcessMessages vermeidet, sollte das soch eh nicht vorkommen!?
[..]
Notnagel: Eine Funktion, die lediglich Resize- und Paint-Nachrichten verarbeitet, statt Application.ProcessMessages einsetzen.

Das ist das Problem dabei, da "unterwegs" immer mal wieder die Statusbar repaintet werden will, wie auch diverse andere Statusanzeigen gibts m.E. kaum eine einfachere Möglichkeit als Application.ProcessMessages. Des Weiteren kommen aber auch via UDP-Messages Nachrichten rein die ggf. Datanbankzugriffe aus- und wieder anschalten kann, bzw. ebenfalls wieder visuelle Informationen direkt an den User weitergibt. Im Prinzip ist es so, dass ich das ganze relativ gut eingrenzen kann auf wenige onExit-Events die offenbar zu der Verwirrung führen. Und da dachte ich eben, wenn ich die aus einer Queue ausfiltern kann, dann reicht das schon, anstatt noch eine Reihe von Flags einzuarbeiten.


@zion
Das werde ich mal ausprobieren.

_________________
mfg. Joel
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mi 17.06.15 18:12 
Also wenn es nur ums Repainten geht, dann wäre vll sowas interessant: stackoverflow.com/a/2183938/1974021

Wenn du das aufrufst (statt Application.ProcessMessages) dann werden nur Paint-Nachrichten verarbeitet, also keine Button-Callbacks oder ähnliches. Natürlich auch keine Tastendrücke.

Wenn das immer noch nicht das ist, was du willst, dann muss ich nochmal nachhaken um dein Problem zu verstehen. Szenario:

1. User klickt Button A. Dieser erledigt viel Arbeit im GUI Thread.
2. User klickt Button B.
3. Callback aus Schritt 1 ruft Application.ProcessMessages auf, was zur Ausführung des Callbacks von Button B führt.
4. Button B soll jetzt "warten" bis das Callback von Button 1 abgeschlossen ist?

Wenn ja: No way. Geht nicht.
Du könntest dir eine eigene ProcessMessages-Funktion schreiben, die einen "Abbruch"-Rückgabewert hat. Aber dann kommst du ja auch nur aus der innersten Funktion raus ;-)
Alternativ kannst du auch eine "zweite Messagequeue" aufmachen, in der du diese Sachen einträgst. Deine eigene ProcessMessages-Funktion incrementiert vor dem Aufruf van Application.ProcessMessages eine globale Variable und fecrementiert danach. Wenn am Ende Wert = 0 ist, eine Message an das eigene Fenster posten. In dem Callback dann nochmal prüfen und ggf. den ersten Wert aus dieser zweiten Queue ausführen.

Zusammengefasst: Mach es halt gleich richtig. Lied: Kurzes Einfrieren kann man vll. in Kauf nehmen, lange Berechnungen in einen Thread auslagern.
JoelH Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 806
Erhaltene Danke: 17

Win10
Delphi Alexandria 11.2 Patch 1
BeitragVerfasst: Do 18.06.15 10:12 
user profile iconjfheins hat folgendes geschrieben Zum zitierten Posting springen:
Also wenn es nur ums Repainten geht, dann wäre vll sowas interessant: stackoverflow.com/a/2183938/1974021

Wenn du das aufrufst (statt Application.ProcessMessages) dann werden nur Paint-Nachrichten verarbeitet, also keine Button-Callbacks oder ähnliches. Natürlich auch keine Tastendrücke.

Das sieht vielversprechend aus, werde ich mal probieren.
user profile iconjfheins hat folgendes geschrieben Zum zitierten Posting springen:

Wenn das immer noch nicht das ist, was du willst, dann muss ich nochmal nachhaken um dein Problem zu verstehen. Szenario:

1. User klickt Button A. Dieser erledigt viel Arbeit im GUI Thread.
2. User klickt Button B.
3. Callback aus Schritt 1 ruft Application.ProcessMessages auf, was zur Ausführung des Callbacks von Button B führt.
4. Button B soll jetzt "warten" bis das Callback von Button 1 abgeschlossen ist?

Wenn ja: No way. Geht nicht.

So ungefähr. Grundsätzlich kann man sagen, das Programm stellt zum großen Teil Eingabemasken zur Verfügung die stets auf onExit (Focusverlust) reagieren und dann unmittelbar die Daten in die Datenbank schreiben. Da die Masken aber ebenso als Suchmasken dienen, muss natürlich sichergestellt sein, in welchem Modus man sich gerade befindet. Dazu kommen des Weiteren Neuanlagen sowie Fremdmanipulationen der Daten durch andere Mitarbeiter, die auch berücksichtigt werden müssen. Des Weiteren muss entsprechend bei Wechseln immer alles in der GUI refreshed werden, da Felder natürlich auch Abhängigkeiten haben und ggf. neu berechnet werden müssen. Unter Umständen hängen da diverse neuerliche Datenbank Zugriffe dahinter sowie informierende Netzwerkkommunikation der Clients untereinander. Und da der User darüber informiert sein soll gibt es natürlich die Statusbarupdates etc. Was alles via Application.ProcessMessages am laufen gehalten wird, aber m.E. auch die Inkonsistenz reinbringt. Der User kann mittels Shortcuts ziemlich schnell durch die verschiedenen Modi springen und dabei eine ungewollte Parallelbearbeitung auslösen, die dann unerwartete Daten schreibt. Zumindest erscheint mir das Szenario so zu sein, da sich das natürlich nicht wirklich debuggen lässt, da beim Debuggen das Programm genug Zeit hat alles zu erledigen, zumal parallel arbeitende User nur schwer in eine solche Session einzubeziehen sind.

Und deshalb meine Frage nach der Queue, denn das Betriebssystem muss ja irgendwo Buch führen was noch alles aussteht, bzw. woran noch gearbeitet wird. Wenn mein Programm diese Liste auslesen könnte, dann könnte es sehr leicht feststellen ob noch etwas aussteht bzw. in Arbeit ist, was einen regelgerechten Ablauf beeinflussen könnte.

user profile iconjfheins hat folgendes geschrieben Zum zitierten Posting springen:


Zusammengefasst: Mach es halt gleich richtig. Lied: Kurzes Einfrieren kann man vll. in Kauf nehmen, lange Berechnungen in einen Thread auslagern.


Es kommt hochgerechnet bei 150.000 Datenmanipulationen einmal vor. Dafür das gesamte Projekt zu redesignen ist der Mühe nicht wert, da ist es einfacher die potenziell fehlerhaften Einträge in der Datenbank grob auszufiltern und bei Bedarf manuell einzuschreiten.

_________________
mfg. Joel