Frage

Ich verwende mehrere kritische Abschnitte in meiner Anwendung. Die kritischen Abschnitte verhindern, dass große Datenblobs gleichzeitig mit verschiedenen Threads geändert und zugegriffen werden.

AFAIK Es funktioniert alles richtig, außer manchmal hängt die Anwendung beim Auslassen. Ich frage mich, ob dies mit meiner Verwendung kritischer Abschnitte zusammenhängt.

Gibt es eine korrekte Möglichkeit, Tcriticalsection -Objekte in einem Destruktor zu befreien?

Danke für alle Antworten. Ich schaue meinen Code noch einmal mit diesen neuen Informationen an. Prost!

War es hilfreich?

Lösung

Wie Rob sagt, ist die einzige Anforderung, dass Sie sicherstellen, dass der kritische Abschnitt derzeit keiner Thread gehört. Nicht einmal der Faden, um es zu zerstören. Es gibt also kein Muster, um eine Tcriticalsection als solche korrekt zu zerstören. Nur ein erforderliches Verhalten, das Ihre Bewerbung Maßnahmen ergreifen muss, um sicherzustellen, dass er eintritt.

Wenn Ihre Bewerbung einsperrt, bezweifle ich, dass es sich um die freie Verantwortung für einen kritischen Abschnitt handelt. Wie MSDN sagt (in dem Link, den Rob veröffentlicht hat), blockiert die Deletecriticalsection () (was letztendlich das freie Aufruf von Tcriticalsection) keine Threads blockiert.

Wenn Sie einen kritischen Abschnitt frei haben, dass andere Themen immer noch versuchten, zugreifen zu können, erhalten Sie Zugriffsverletzungen und andere unerwartete Verhaltensweisen, nicht mit Deadlocks, da diese kleine Code -Probe Ihnen helfen sollte, nachzuweisen:

implementation

uses
  syncobjs;


  type
    tworker = class(tthread)
    protected
      procedure Execute; override;
    end;


  var
    cs: TCriticalSection;
    worker: Tworker;


procedure TForm2.FormCreate(Sender: TObject);
begin
  cs := TCriticalSection.Create;

  worker := tworker.Create(true);
  worker.FreeOnTerminate := TRUE;
  worker.Start;

  sleep(5000);

  cs.Enter;

  showmessage('will AV before you see this');
end;

{ tworker }

procedure tworker.Execute;
begin
  inherited;
  cs.Free;
end;

Fügen Sie der Implementierungseinheit eines Formulars hinzu und korrigieren Sie die Referenz "tform2" für den Ereignishandler von FormCreate () nach Bedarf.

In FormCreate () wird ein kritischer Abschnitt erstellt, der einen Thread startet, dessen alleiniger Zweck es ist, diesen Abschnitt zu befreien. Wir stellen eine Verzögerung von Sleep.

Wir können natürlich nicht, weil es frei war. Aber unser Code hängt nicht auf - er ist nicht versuchend, auf eine Ressource zuzugreifen, die etwas anderes gehört. Er bläst einfach in die Luft, weil wir versuchen, auf eine Ressource zuzugreifen, die nicht mehr existiert.

Sie könnten in diesem Szenario noch sicherer sein, ein AV zu erstellen, indem Sie die kritische Abschnittsreferenz null tun, wenn es frei ist.

Versuchen Sie nun, den Code von FormCreate () zu diesem Zeitpunkt zu ändern:

  cs := TCriticalSection.Create;

  worker := tworker.Create(true);
  worker.FreeOnTerminate := TRUE;

  cs.Enter;
  worker.Start;

  sleep(5000);

  cs.Leave;

  showmessage('appearances can be deceptive');

Dies ändert die Dinge ... jetzt übernimmt der Haupt -Thread den kritischen Abschnitt - der Arbeiter -Thread befreien nun den kritischen Abschnitt, während er noch im Haupt -Thread gehört.

In diesem Fall führt der Anruf bei Cs.leave jedoch nicht unbedingt zu Zugangsverletzungen. Alles, was in diesem Szenario (AFAICT) passiert, ist, dass der Besitzfaden den Abschnitt so "verlassen" darf, wie er es erwarten würde (er ist natürlich nicht, weil der Abschnitt verschwunden ist, aber es scheint zu dem Thread, den es den zuvor eingegebenen Abschnitt verlassen hat) ...

... In komplexeren Szenarien ist wahrscheinlich ein Zugriffsverletzung oder ein anderer Fehler wahrscheinlich, da der Speicher, der zuvor für das Objekt des kritischen Abschnitts verwendet wurde Rufen Sie ein anderes unbekanntes Objekt oder Zugriff auf ungültige Speicher usw. an usw.

Wiederum wechseln Sie den Worker.execute (), damit er den kritischen Abschnitt Ref nach dem freien Vorsprung ist, einen Zugriffsverletzung beim Versuch, cs.leave () aufzurufen, da Leave () Release () und Release () () aufrufe () () aufzurufen. ist virtuell - eine virtuelle Methode mit einer NIL -Referenz zu rufen, wird AV (dito für Enter () garantiert, der die Virtual Acquire () -Methode nennt).

In jedem Fall:

Schlimmster Fall: eine Ausnahme oder ein seltsames Verhalten

"Bester" Fall: Der besitzende Thread scheint zu glauben, dass er den Abschnitt als normal "verlassen" hat.

In keinem Fall ist ein Deadlock oder ein Hang, das einfach auftreten wird, wenn ein kritischer Abschnitt in einem Thread frei ist, wenn andere Threads versuchen, diesen kritischen Abschnitt einzugeben oder zu lassen.

All dies ist eine Runde, um zu sagen, dass es sich so anhört, als hätten Sie eine grundlegendere Rennbedingung in Ihrem Threading-Code, die nicht direkt mit der freien Ausführung Ihrer kritischen Abschnitte zusammenhängt.

Auf jeden Fall hoffe ich, dass meine kleine Ermittlungsarbeit Sie auf den richtigen Weg einräumt.

Andere Tipps

Stellen Sie einfach sicher, dass nichts noch den kritischen Abschnitt besitzt. Andernfalls, MSDN erklärt, "Der Zustand der Threads, die auf das Eigentum des gelöschten kritischen Abschnitts warten, ist undefiniert." Ansonsten rufen Sie an Free Wie du tun es mit allen anderen Objekten.

AFAIK Es funktioniert alles richtig, außer manchmal hängt die Anwendung beim Auslassen. Ich frage mich, ob dies mit meiner Verwendung kritischer Abschnitte zusammenhängt.

Ja, so ist es. Das Problem ist jedoch wahrscheinlich nicht in der Zerstörung. Sie haben wahrscheinlich einen Deadlock.

Deadlocks sind, wenn zwei Themen auf zwei exklusiven Ressourcen warten, die jeweils beide wollen und jeweils nur einen besitzen:

//Thread1:
FooLock.Enter;
BarLock.Enter;

//Thread2:
BarLock.Enter;
FooLock.Enter;

Der Weg, um diese zu bekämpfen, besteht darin, Ihre Schlösser zu bestellen. Wenn ein Thread zwei davon will, muss er sie nur in bestimmter Reihenfolge eingeben:

//Thread1:
FooLock.Enter;
BarLock.Enter;

//Thread2:
FooLock.Enter;
BarLock.Enter;

Auf diese Weise tritt Deadlock nicht auf.

Viele Dinge können Deadlock auslösen, nicht nur zwei kritische Abschnitte. Zum Beispiel haben Sie möglicherweise SendMessage (Synchronous Message Dispatch) oder Delphis Synchronize und einen kritischen Abschnitt verwendet:

//Thread1:
OnPaint:
  FooLock.Enter;
  FooLock.Leave;

//Thread2:
FooLock.Enter;
Synchronize(SomeProc);
FooLock.Leave;

Synchronisieren und sendMessage Senden Sie Nachrichten an Thread1. Um diese Nachrichten zu entsenden, muss Thread1 alle Arbeiten beenden. Zum Beispiel Onpaint -Handler.

Um das Malerei zu beenden, braucht es Foolock, das von Thread2 aufgenommen wird und auf Thread1 wartet, um das Gemälde zu beenden. Sackgasse.

Die Art und Weise, dies zu lösen, besteht darin, entweder Synchronize und SendMessage (den besten Weg) zu verwenden oder sie zumindest außerhalb von Schlössern zu verwenden.

Gibt es eine korrekte Möglichkeit, Tcriticalsection -Objekte in einem Destruktor zu befreien?

Es spielt keine Rolle, wo Sie Tcriticalsection in einem Destruktor befreien oder nicht.

Bevor Sie jedoch die Tcriticalsection befreien, müssen Sie sicherstellen, dass alle Themen, die sie hätten verwenden können, gestoppt sind oder sich in einem Zustand befinden, in dem sie möglicherweise nicht mehr versuchen können, diesen Abschnitt zu betreten.

Wenn Ihr Thread beispielsweise in diesen Abschnitt eingeht, während Sie eine Netzwerknachricht versenden, müssen Sie sicherstellen, dass das Netzwerk getrennt wird und alle ausstehenden Nachrichten verarbeitet werden.

Das Versäumnis, dies zu tun, wird in den meisten Fällen Zugangsverstöße auslösen, manchmal nichts (wenn Sie Glück haben) und selten Deadlocks.

Es gibt keine magische Verwendung von Tcriticalsection sowie in kritischen Abschnitten selbst. Versuchen Sie, Tcriticalsection -Objekte durch einfache API -Aufrufe zu ersetzen:

uses
  Windows, ...

var
  CS: TRTLCriticalSection;

...

EnterCriticalSection(CS);
....
here goes your code that you have to protect from access by multiple threads simultaneously
...
LeaveCriticalSection(FCS);
...

initialization
  InitializeCriticalSection(CS);

finalization
  DeleteCriticalSection(CS);

Wenn Sie auf API wechseln, werden Sie die Klarheit Ihres Codes nicht schaden, sondern dazu beitragen, versteckte Fehler zu enthüllen.

Sie müssen alle kritischen Abschnitte mit einem Versuch schützen.

Verwenden Sie Trtlcriticalsection anstelle einer Tcriticalsection -Klasse. Es ist plattformübergreifend und Tcriticalsection ist nur eine unnötige Verpackung um sie herum.

Wenn während des Datenprozesses eine Ausnahme auftritt, ist der kritische Abschnitt nicht übrig und ein anderer Thread kann blockieren.

Wenn Sie eine schnelle Antwort wünschen, können Sie auch TryEntercriticalSection für einen Benutzeroberflächenprozess oder dergleichen verwenden.

Hier sind einige gute Übungsregeln:

  1. Machen Sie Ihre Trtlcriticalsection zu einer Eigenschaft einer Klasse;
  2. Rufen Sie die Initializizecriticalsection im Klassenkonstruktor an und deletekritikalen Unterricht im Klassenzerstörer;
  3. Verwenden Sie Entercriticalsection () ... Versuchen Sie es ... tun Sie etwas ... endlich Leavecriticalsection (); Ende;

Hier ist ein Code -Beispiel:

type
  TDataClass = class
  protected
    fLock: TRTLCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure SomeDataProcess;
  end;

constructor TDataClass.Create;
begin
  inherited;
  InitializeCriticalSection(fLock);
end;

destructor TDataClass.Destroy;
begin
  DeleteCriticalSection(fLock);
  inherited;
end;

procedure TDataClass.SomeDataProcess;
begin
  EnterCriticalSection(fLock);
  try
    // some data process
  finally
    LeaveCriticalSection(fLock);
  end;
end;

Wenn der einzige explizite Synchronisierungscode in Ihrer App durch kritische Abschnitte erfolgt, sollte es nicht zu schwierig sein, dies aufzunehmen.

Sie geben an, dass Sie die Deadlock nur bei der Kündigung gesehen haben. Dies bedeutet natürlich nicht, dass es während des normalen Betriebs Ihrer App nicht passieren kann, aber meine Vermutung (und wir müssen ohne weitere Informationen erraten) ist, dass es ein wichtiger Hinweis ist.

Ich würde die Hypothese haben, dass der Fehler mit der Art und Weise zusammenhängt, in der Threads gewaltsam gekündigt werden. Ein Deadlock wie Sie beschreiben, würde passieren, wenn ein Faden endet, während er das Schloss hält, aber dann versuchte ein anderer Faden, das Schloss zu erwerben, bevor er die Chance hatte, zu beenden.

Eine sehr einfache Sache, die das Problem sofort beheben kann, besteht darin, sicherzustellen, dass andere, wie andere gesagt haben, alle Verwendungen des Schlosses durch Versuch geschützt sind. Dies ist wirklich ein kritischer Punkt.

In Delphi gibt es zwei Hauptmuster für die Lebensdauerverwaltung von Ressourcen wie folgt:

lock.Acquire;
Try
  DoSomething();
Finally
  lock.Release;
End;

Das andere Hauptmuster ist die Paarung/Freisetzung/Freisetzung in Create/Destroy, aber das ist bei Schlösser weitaus seltener.

Angenommen, Ihr Verwendungsmuster für die Schlösser ist, wie ich vermute (dh Erwerbsfreisetzung innerhalb derselben Methode), dass alle Verwendungen durch Versuch geschützt sind?

Wenn Ihre Bewerbung nur beim Ausgang hängt/ Deadlocks auf dem Ausstieg hängt bitte das Onterterate -Ereignis für alle Threads. Wenn der Hauptfaden für die anderen Fäden endet und dann darauf wartet, bevor sie sie befreien. Es ist wichtig, keine synchronisierten Anrufe im On -Termin -Ereignis zu tätigen. Dies kann zu einem toten Schloss führen, da der Hauptfaden darauf wartet, dass der Arbeiter -Faden beendet wird. Der Synchronisierungsaufruf wartet jedoch auf den Hauptfaden.

Löschen Sie keine kritischen Abschnitte bei Objektdestruktor. Manchmal zum Absturz Ihrer Bewerbung.

Verwenden Sie eine separate Methode, die den kritischen Abschnitt löscht.

verfahren
Start
Deletecriticalsection (CriticalSektion);
Ende;

destructor tonObject.destroy ();
Start
// mache deine Release -Aufgaben hier
Ende;

1) Sie rufen den kritischen Abschnitt löschen an
2) Nachdem Sie das Objekt freigelassen haben (frei)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top