2010-06-11 5 views
13

J'écris une application multithread dans Delphi et ai besoin d'utiliser quelque chose pour protéger les ressources partagées.Delphi - Existe-t-il un verrou C# équivalent?

En C# j'utiliser le « verrouiller » mot-clé: méthode

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

Dans Delphi je ne pouvais pas trouver quelque chose de semblable, je trouve juste TThread.Synchronize (someMethod), ce qui empêche les conflits potentiels en appelant someMethod dans le thread principal VCL, mais ce n'est pas exactement ce que je veux faire ....

Edit: J'utilise Delphi 6

+1

vous pouvez appeler au moins l'API Windows Les sections critiques – Arseny

Répondre

17

(Un) heureusement, vous ne pouvez pas verrouiller sur des objets quelconques dans Delphi 6 (bien que vous pouvez dans les versions plus récentes, 2009 et plus tard), vous devez donc avoir un objet de verrouillage séparé, généralement une section critique.

TCriticalSection (note: la documentation est de FreePascal, mais il existe dans Delphi ainsi):

Exemple Code:

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 a introduit la possibilité d'obtenir un verrou sur un objet - « Pourquoi a la taille de TObject Dans Delphi 2009 Doublement » voir à http://blogs.teamb.com/craigstuntz/2009/03/25/38138/ – mjn

+2

Ouais, j'ai appris quelque chose plus tard, mais la question avait déjà été éditée pour mentionner delphi 6, donc je n'ai pas pris la peine de mettre à jour ma réponse juste pour le remettre sur la première page. J'aurais dû laisser un commentaire cependant. Mais je ferai une note pour dire que je m'oppose à l'opinion exprimée dans cet article. La possibilité de verrouiller un objet n'est pas une bonne chose, et peut facilement conduire à des blocages si vous ne savez pas ce que vous faites. Puisque n'importe qui peut verrouiller sur n'importe quel objet, parfois ils le font. Il est préférable d'allouer spécifiquement des objets verrouillés et de les utiliser. Pas de surprises (à cet égard.) –

0

Comme l'a dit, pour le code court, ce qui ne remet pas à l'extérieur la portée locale et n'acquiert pas d'autres verrous, vous pouvez utiliser des sections critiques via SyncObjs.TCriticalSection,
pour le code plus long/plus compliqué que vous pouvez utiliser SyncObjs.TMutex, qui est attendu (avec délai), ne stagne pas si le fil propriétaire meurt et peut être partagé par nom avec d'autres processus.
L'utilisation de ces wrappers facilite les modifications de la couche de synchronisation.

Dans tous les cas, méfiez-vous des dragons ici: my answer to Difference between the WaitFor function for TMutex delphi and the equivalent in win32 API

3

Bien que pas tout à fait aussi facile que C#, suivant pourrait travailler pour vous.

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

En un mot

  • une liste est conservée pour chaque exemple que vous souhaitez protéger.
  • lorsqu'un second thread connait le Lock(mySharedObj), la recherche d'un verrou existant est effectuée dans la liste interne. Un nouveau verrou sera créé si aucun verrou existant n'est trouvé. Le nouveau thread sera bloqué si un autre thread a encore le verrou.
  • le Unlock est nécessaire parce que nous ne pouvons pas être sûr que la référence à l'instance ILock seulement sortira de la portée à la fin de la méthode appelant Lock. (Si nous pouvions, le Unlock pourrait être enlevé).

Notez que dans cette conception, un TLOCK est créé pour chaque instance d'objet que vous souhaitez protéger, sans qu'il soit libéré jusqu'à ce que l'application se termine.
Cela pourrait être pris en compte, mais cela impliquerait de déconner avec _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

la liste des verrous devrait également être triée en quelque sorte et le "localiser" fait par la recherche binaire pour l'amélioration des performances. –

+0

@Ken Bourassa: Il est vrai qu'il y a encore beaucoup de place à l'amélioration. L'objectif était simplement de montrer comment une construction similaire à celle utilisée dans C# pouvait être faite avec Delphi 6. –

11

Il n'y a pas d'équivalent dans Delphi 6. de Delphi 2009, vous pouvez utiliser les méthodes System.TMonitor pour saisir les verrous sur les objets arbitraires.

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

(Vous devez le préfixe « système » parce que les conflits de noms TMonitor avec le type dans l'unité Forms. L'alternative est d'utiliser les MonitorEnter et MonitorExit globales fonctions.)

0

Utilisation des aides de classe, vous pouvez utilisez ceci. Ne fonctionnera pas avec les versions plus anciennes cependant. Mais je conseillerais d'utiliser TMonitor uniquement dans XE5. Depuis c'est beaucoup plus lent que 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;