2010-06-11 5 views
13

Ich schreibe eine Multithread-Anwendung in Delphi und muss etwas zum Schutz freigegebener Ressourcen verwenden.Delphi - Gibt es eine Entsprechung zu C# -Sperre?

In C# würde ich das „Schloss“ Schlüsselwort:

private someMethod() { 
    lock(mySharedObj) { 
     //...do something with mySharedObj 
    } 
} 

In Delphi kann ich nichts Vergleichbares finden, fand ich gerade TThread.Synchronize (irgendeinemethode) -Methode, die durch mögliche Konflikte verhindert Aufruf in irgendeinemethode VCL-Haupt-Thread, aber es ist nicht genau das, was ich tun will ....

Edit: ich bin mit Delphi 6

+1

Mindestens können Sie Windows-API aufrufen Kritische Abschnitte – Arseny

Antwort

17

(Un) Glück kann man nicht sperren auf beliebige Objekte in Delphi 6 (obwohl Sie in neueren Versionen, 2009 und später), so müssen Sie haben ein separates Sperrobjekt, normalerweise ein kritischer Abschnitt.

TCriticalSection (Anmerkung: die Dokumentation ist von Freepascal, aber es existiert auch in Delphi):

Beispielcode:

type 
    TSomeClass = class 
    private 
    FLock : TCriticalSection; 
    public 
    constructor Create(); 
    destructor Destroy; override; 

    procedure SomeMethod; 
    end; 

constructor TSomeClass.Create; 
begin 
    FLock := TCriticalSection.Create; 
end; 

destructor TSomeClass.Destroy; 
begin 
    FreeAndNil(FLock); 
end; 

procedure TSomeClass.SomeMethod; 
begin 
    FLock.Acquire; 
    try 
    //...do something with mySharedObj 
    finally 
    FLock.Release; 
    end; 
end; 
+1

Delphi 2009 die Möglichkeit, eine Sperre auf ein beliebiges Objekt zu bekommen eingeführt - „Warum hat die Größe von TObject In Delphi 2009 Verdoppelt“ sehen http://blogs.team.com/craigstuntz/2009/03/25/38138/ – mjn

+2

Ja, ich habe später etwas darüber erfahren, aber die Frage wurde bereits bearbeitet, um Delphi 6 zu erwähnen, also habe ich mich nicht darum gekümmert, meine Antwort zu aktualisieren nur um es auf die Titelseite zurück zu stoßen. Ich hätte allerdings einen Kommentar hinterlassen sollen. Aber, ich werde eine Notiz machen, um zu sagen, dass ich Opposition zu der Ansicht in diesem Artikel nehme. Die Fähigkeit, jedes Objekt zu sperren, ist * nicht * eine gute Sache und kann leicht zu Deadlocks führen, wenn Sie nicht wissen, was Sie tun. Da irgendjemand jedes Objekt sperren kann, tun sie es manchmal. Es ist viel besser, Sperrobjekte gezielt zuzuordnen und zu verwenden. Keine Überraschungen (in dieser Hinsicht.) –

0

Wie gesagt, für kurzen Code, der Anruf nicht außerhalb der lokale Bereich und keine anderen Sperren, können Sie kritische Abschnitte über SyncObjs.TCriticalSection,
für längere/komplizierter Code verwenden, die Sie verwenden können SyncObjs.TMutex, die Waitable (mit Timeout) ist, nicht zum Stillstand, wenn der besitzende Thread stirbt und kann by-name mit anderen Prozessen geteilt werden.
Durch die Verwendung dieser Wrapper werden Änderungen an der Synchronisationsschicht erleichtert.

In allen Fällen passen sie von Drachen hier: my answer to Difference between the WaitFor function for TMutex delphi and the equivalent in win32 API

3

Obwohl es nicht ganz so einfach, wie C#, folgende könnte für Sie arbeiten.

with Lock(mySharedObj) do 
    begin 
    //...do something with mySharedObj 
    UnLock; 
    end; 

Auf den Punkt gebracht

  • einer Liste wird für jede Instanz gehalten Sie schützen möchten.
  • Wenn ein zweiter Thread die Lock(mySharedObj) kursiert, wird die interne Liste nach einer vorhandenen Sperre durchsucht. Eine neue Sperre wird erstellt, wenn keine vorhandene Sperre gefunden wird. Der neue Thread wird blockiert, wenn ein anderer Thread noch die Sperre hat.
  • Die Unlock ist erforderlich, da wir nicht sicher sein können, dass der Verweis auf die ILock-Instanz nur außerhalb des Bereichs am Ende der Methode Lock aufrufen wird. (Wenn wir könnten, könnte die Unlock entfernt werden).

Beachten Sie, dass in diesem Entwurf, ein TLOCK für jede Objektinstanz erstellt wird, ohne sie schützen wollen befreit werden, bis die Anwendung beendet wird.
Dies könnte in faktorisiert werden, aber es würde bedeuten, rumspielen mit _AddRef & _Release.


unit uLock; 

interface 

type 
    ILock = interface 
    ['{55C05EA7-D22E-49CF-A337-9F989006D630}'] 
    procedure UnLock; 
    end; 

function Lock(const ASharedObj: TObject): ILock; 

implementation 

uses 
    syncobjs, classes; 

type 
    _ILock = interface 
    ['{BAC7CDD2-0660-4375-B673-ECFA2BA0B888}'] 
    function SharedObj: TObject; 
    procedure Lock; 
    end; 

    TLock = class(TInterfacedObject, ILock, _ILock) 
    private 
    FCriticalSection: TCriticalSection; 
    FSharedObj: TObject; 
    function SharedObj: TObject; 
    public 
    constructor Create(const ASharedObj: TObject); 
    destructor Destroy; override; 
    procedure Lock; 
    procedure UnLock; 
    end; 

var 
    Locks: IInterfaceList; 
    InternalLock: TCriticalSection; 

function Lock(const ASharedObj: TObject): ILock; 
var 
    I: Integer; 
begin 
    InternalLock.Acquire; 
    try 
    //***** Does a lock exists for given Shared object 
    for I := 0 to Pred(Locks.Count) do 
     if (Locks[I] as _ILock).SharedObj = ASharedObj then 
     begin 
     Result := ILock(Locks[I]); 
     Break; 
     end; 

    //***** Create and add a new lock for the shared object 
    if not Assigned(Result) then 
    begin 
     Result := TLock.Create(ASharedObj); 
     Locks.Add(Result); 
    end; 
    finally 
    InternalLock.Release; 
    end; 
    (Result as _ILock).Lock; 
end; 

{ TLock } 

constructor TLock.Create(const ASharedObj: TObject); 
begin 
    inherited Create; 
    FSharedObj := ASharedObj; 
    FCriticalSection := TCriticalSection.Create; 
end; 

destructor TLock.Destroy; 
begin 
    FCriticalSection.Free; 
    inherited Destroy; 
end; 

procedure TLock.Lock; 
begin 
    FCriticalSection.Acquire; 
end; 

function TLock.SharedObj: TObject; 
begin 
    Result := FSharedObj; 
end; 

procedure TLock.UnLock; 
begin 
    FCriticalSection.Release; 
end; 

initialization 
    Locks := TInterfaceList.Create; 
    InternalLock := TCriticalSection.Create; 

finalization 
    InternalLock.Free; 
    Locks := nil 

end. 
+0

die Locks-Liste müsste auch irgendwie sortiert werden und das "Lokalisieren" durch binäre Suche zur Leistungsverbesserung getan. –

+0

@Ken Bourassa: Es stimmt, es gibt noch viel Raum für Verbesserungen. Das Ziel war jedoch nur zu zeigen, wie eine ähnliche Konstruktion wie in C# mit Delphi 6 gemacht werden kann. –

11

gibt es keine Entsprechung in Delphi 6. Ab Delphi 2009 können Sie die System.TMonitor Methoden verwenden Sperren für beliebige Objekte zu greifen.

System.TMonitor.Enter(obj); 
try 
    // ... 
finally 
    System.TMonitor.Exit(obj); 
end; 

(Sie müssen das „System“ Präfix, weil die TMonitor Namenskonflikte mit dem in der Einheit bildet. Die Alternative, die globalen MonitorEnter und MonitorExit Funktionen zu verwenden ist.)

0

Mit Klasse Helfer möglich benutze das. Funktioniert jedoch nicht mit älteren Versionen. Aber ich würde TMonitor nur in XE5 empfehlen. Da ist es ziemlich viel langsamer als TRTLCriticalSection.

http://www.delphitools.info/2013/06/06/tmonitor-vs-trtlcriticalsection/

THelper = class helper for TObject 
    procedure Lock; 
    procedure Unlock; 
end; 

procedure THelper.Lock; 
begin 
    System.TMonitor.Enter(TObject(Self)); 
end; 

procedure THelper.Unlock; 
begin 
    System.TMonitor.Exit(TObject(Self)); 
end; 
Verwandte Themen