Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Prozedur gegen sich selbst absichern (https://www.delphipraxis.net/131608-prozedur-gegen-sich-selbst-absichern.html)

DelphiManiac 27. Mär 2009 12:45


Prozedur gegen sich selbst absichern
 
Hallo,

ich habe folgendes Problem und hoffe ihr könnt mir da helfen:


Ich habe eine Prozedur:
Delphi-Quellcode:
procedure TuWas;
begin
  // Prozedur tut was und hat einen exklusiven Zugriff auf eine Ressource
  // zwischendurch ruft sie Application.ProcessMessages auf
end;
Jetzt wird diese Methode von mehreren Seiten aufgerufen (Button.Clicks, oder Timmereignisse etc).
Da es schwierig ist diese zu synchronisieren würde ich gerne eine Möglichkeit schaffen, dass die Prozedur nur exklusiv aufgerufen wird.

Jetzt kann folgendes passieren:

- Button.Click
-> TuWas wird aufgerufen (das dauert u.Um. 100 ms und zwischendurch wird Application.Processmessages aufgerufen)
->Timer Ereignis schlägt zu und ruft auch TuWas auf.

Jetzt ist kein konsistenter Zustand mehr gegeben.

Ich wollte euch fragen welche Form von Sperrobjekten man da verwenden kann schließlich ist ja alles im einem Thread und einem Prozess?


Vielen Dank

MfG
DelphiManiac

Bernhard Geyer 27. Mär 2009 12:49

Re: Prozedur gegen sich selbst absichern
 
Das einfachste ist ein Flag (IchBinInTuWas: Boolean) das in der Unit definiert wird und am Anfang geprüft, anschließend gesetzt und am Ende zurück gesetzt wird.

SirThornberry 27. Mär 2009 12:49

Re: Prozedur gegen sich selbst absichern
 
Du kannst über eine globale Variable (welche im Implementationsteils plaziert werden sollte) absichern das die Funktion nicht mehrfach aufgerufen wird.
Was mir bei deiner Beschreibung fehlt: Was soll passieren die Funktion bereits aktitv ist?
Und anstelle von ProcessMessages wäre hier vielleicht ein Thread sinnvoller? Dann sollte der Zugriff über CriticalSections erfolgen.

[Edit]Du schreibst
Zitat:

Jetzt wird diese Methode...
Wenn es sich wirklich um eine Methode handelt (was hier wirklich sinnvoll wäre) dann gehört die Variable natürlich in den private-Bereich. Wenn es sich jedoch um eine Ressource handelt auf die auch nicht von mehreren Instanzen zugegriffen werden soll dann gehört sie wieder global so das die Methode (egal von welcher Instanz) nur einmal zur gleichen zeit aufgerufen werden kann. In dem Fall bietet es sich auch an den Zugriff auf die Ressource über eine eigene Komponente zu realisieren und über diese das ganze abzusichern.

DelphiManiac 27. Mär 2009 13:07

Re: Prozedur gegen sich selbst absichern
 
Hallo,

also einen Thread würde ich an der Stelle recht ungern einpflegen, weiß, dass das im Prinzip die sauberste Lösung wäre, aber das bringt noch eine Reihe anderer Probleme mit sich, und da es eine Programmerweiterung sein soll, sind mir hier glaube ich die Hände gebunden...

Also wenn die Methode noch aktiv ist sollen "die" anderen warten, also müsste das im Prinzip in eine Queue gehen die nach FIFO die Aufrufe macht, wenn die Methode nicht mehr besetzt ist, so macht es doch auch die Windows Queue, aber da kann ja der Methoden aufruf durch dass Appl.Processmessages dazwischen kommen.

Gruß

SirThornberry 27. Mär 2009 13:17

Re: Prozedur gegen sich selbst absichern
 
Das warten gestaltet sich sehr schwierig/unmöglich? wenn du eine Threads arbeitest. Denn wenn Du ProcessMessages aufrufst und dann im Timerevent wartest (weil die Funktion bereits belegt ist) dann kehrt ProcessMessages auch nicht zurück und somit wird die Funktion nie fertig. Warten ist also nur richtig möglich wenn es sich um mehrere Threads handelt.

DelphiManiac 27. Mär 2009 13:37

Re: Prozedur gegen sich selbst absichern
 
Ok, angenommen ich würde dieses eine Prozedur in einen Thread auslagern,

wie sollte ich dann die Absicherung machen, über Critical Sections?

Gruß
DM

Reinhard Kern 27. Mär 2009 13:41

Re: Prozedur gegen sich selbst absichern
 
Zitat:

Zitat von DelphiManiac
...
Also wenn die Methode noch aktiv ist sollen "die" anderen warten, also müsste das im Prinzip in eine Queue gehen die nach FIFO die Aufrufe macht, wenn die Methode nicht mehr besetzt ist, so macht es doch auch die Windows Queue, aber da kann ja der Methoden aufruf durch dass Appl.Processmessages dazwischen kommen.

Gruß

Hallo,

du siehst das schon nebelhaft richtig, wenn Tuwas zwar exklusiv ist, aber nach Aufruf immer abgearbeitet werden muss, dann muss man wohl die Programmstruktur nicht unerheblich ändern und für Tuwas eine Warteschlange einrichten - die Aufrufer platzieren dort also ihren Wunsch nach Tuwas und die Parameter dazu. Das Problem setzt sich aber fort: können die Aufrufer dann weitermachen oder müssen sie warten, bis ihr Tuwas fertig ist? Das erhöht das Deadlock-Potential gewaltig und setzt sorgfältige Planung voraus.

Da Windows (inzwischen) preemptive Multitasking verwendet, kann man auch mit einer einfachen boolschen variable in Schwierigkeiten kommen, daher solltest du dich mit den Möglichkeiten des BS beschäftigen wie Mutexe, Semaphoren, Critical Sections usw. Ist komplex, aber das liegt halt am Problem.

Gruss Reinhard

mjustin 27. Mär 2009 13:41

Re: Prozedur gegen sich selbst absichern
 
Zitat:

Zitat von DelphiManiac
Ich wollte euch fragen welche Form von Sperrobjekten man da verwenden kann

Aus der Indy Library:

TIdThreadSafeBoolean

(in IdThreadSafe.pas)

Cheers,

jfheins 27. Mär 2009 13:42

Re: Prozedur gegen sich selbst absichern
 
Zitat:

Zitat von DelphiManiac
Ok, angenommen ich würde dieses eine Prozedur in einen Thread auslagern,

wie sollte ich dann die Absicherung machen, über Critical Sections?

Gruß
DM

Ja zum Beispiel ;)

Wenn du sie in einen Thread auslagerst, kannst du dir das Application.Provessmessages sparen, da die Hauptanwendung ja sowiso weiterläuft.

Dann musst du dir ein CriticalSection-Objekt erstellen und am anfang der Prozedur da rein gehen (weis nicht, wie die Methode genau heißt) Falls die Critical Section belegt ist, wird entsprechend gewartet.

Am Ende musst du sie verlassen, was es dann den wartenden ermöglicht, ihrerseits einzutreten.

Meflin 27. Mär 2009 13:44

Re: Prozedur gegen sich selbst absichern
 
Zitat:

Zitat von jfheins
... am anfang der Prozedur da rein gehen (weis nicht, wie die Methode genau heißt) ... Am Ende musst du sie verlassen

TCriticalSection.Enter und .Leave ;)

DelphiManiac 27. Mär 2009 13:52

Re: Prozedur gegen sich selbst absichern
 
Wenn ich das so mache habe ich aber leider ein Problem:


Ich habe ja gesagt, dass ich z.B.: die Methode im Button aufrufe,
Delphi-Quellcode:
procedure TMainGUI.btnSaveDichteDampClick(Sender: TObject);
begin
  Screen.Cursor:=crHourGlass;
  if not(TuWas() = 0)) then
  begin
//  Fehler ausgeben
  end;
  Screen.Cursor:=crDefault;
end;
Jetzt wird ja im Prinzip durch das zurücksetzen des Cursors signalisiert, dass die Methode fertig ausgeführt wurde, wenn ich das jetzt im Thread mache, habe ich diese direkte Rückkopplung nicht...

Dann müsste ich wieder auf den Thread warten...
//edit : Delphicode geändert

jfheins 27. Mär 2009 13:55

Re: Prozedur gegen sich selbst absichern
 
Du kannst mit MSDN-Library durchsuchenMsgWaitForMultipleObjects auf Messages und den Thread warten ;)

Wenn messages eintreten, kannst du diese abarbeiten lassen, und wieder warten, wenn der Thread fertig ist, den Cursor wieder ändern ...

Ungefähr sowas: http://www.delphipraxis.net/internal...=696510#696510 ;)

DelphiManiac 27. Mär 2009 14:13

Re: Prozedur gegen sich selbst absichern
 
Also im Prinzip gibt mir die Methode immer einen Wert zurück, den ich nach der Ausführung der Mehtode benötige.
Z.B.: um Ihn anzuzeigen.

Wenn ich jetzt einen Thread mache, obwohl ich warten will und dann doch wieder auf den Thread warte habe ich irgenwie Bauchschmerzen bei dem Programmdesign...

OldGrumpy 27. Mär 2009 14:35

Re: Prozedur gegen sich selbst absichern
 
Nach der diffusen Problembeschreibung würde ich das mal so grob aus dem Bauch heraus wie folgt lösen:

- TuWas wird über einen Dispatcher aufgerufen (dazu gleich mehr)
- Der Dispatcher sorgt dafür, dass TuWas nur von ihm aufgerufen wird und mittels CriticalSection oder passendem anderen Mechanismus wird verhindert, dass TuWas zweimal parallel laufen kann.
- Der Mauscursor wird am Anfang von TuWas auf Sanduhr gesetzt und als letztes in TuWas wieder normalisiert
- Aufrufe von TuWas werden nicht direkt über Buttonclicks o.ä. erledigt sondern als Aufrufe an den Dispatcher, der daraufhin einen weiteren Eintrag in seine Worklist macht statt TuWas direkt aufzurufen
- Über einen Timer wird in regelmäßigen Intervallen geprüft ob TuWas noch beschäftigt ist, ansonsten wird TuWas erneut gemäß den Daten in der Warteliste ein weiteres Mal aufgerufen, bis die Liste abgearbeitet ist. Danach kann der Timer deaktiviert werden bis ein erneuter Aufruf des Dispatchers für neue Arbeit für TuWas sorgt.

Wenn man es ganz ordentlich machen wollte, müsste das ganze noch etwas anders aussehen, indem das ganze komplett eventbasiert abläuft. Das erhöht aber wiederum die Gefahr von Deadlocks ganz enorm und bedarf sorgfältiger Vorplanung. Wenn ich mir die Beschreibung nochmal durchlese, so komme ich zu dem Schluss, dass - auch wenn der Auftrag nur "Programmerweiterung" heisst - das ganze nicht ohne Review der bestehenden Konstruktion laufen sollte. Man kann vieles umsetzen, nur ist nicht alles technisch machbare auch sinnvoll :)

shmia 27. Mär 2009 14:42

Re: Prozedur gegen sich selbst absichern
 
Zitat:

Zitat von DelphiManiac
Delphi-Quellcode:
procedure TuWas;
begin
  // Prozedur tut was und hat einen exklusiven Zugriff auf eine Ressource
  // zwischendurch ruft sie Application.ProcessMessages auf
end;

Deine Procedure darf nicht einfach Application.ProcessMessages aufrufen!
Sie braucht einen Callback-Mechanismus, damit du genau steuern kannst, was passieren soll.
Delphi-Quellcode:
procedure TuWas(callback:TNotifyEvent);
begin
  // Prozedur tut was und hat einen exklusiven Zugriff auf eine Ressource
  // zwischendurch ruft sie den Callback auf
  for .... do
  begin
    if Assigned(callback) then callback(nil);
    // ....
  end;
end;
Das Callback kann als Procedure oder als Methode ausgeführt werden.
Ich habe hier mal eine Methode (TNotifyEvent) gewählt, weil ich mir gut vorstellen kann,
dass "TuWas" so komplex ist, dass man daraus eine Klasse machen müsste.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:56 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 by Thomas Breitkreuz