2010-06-11 16 views
13

Escribo una aplicación multiproceso en Delphi y necesito usar algo para proteger los recursos compartidos.Delphi - ¿Hay algún equivalente al bloqueo de C#?

En C# que haría uso de la "cerradura" palabra clave:

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

En Delphi no pude encontrar algo similar, me encontré solo método TThread.Synchronize (algunMetodo), lo que evita posibles conflictos llamando algunMetodo en hilo principal de VCL, pero no es exactamente lo que quiero hacer ....

Editar: estoy usando Delphi 6

+1

Al menos se puede llamar a la API de Windows Las secciones críticas – Arseny

Respuesta

17

(Un), afortunadamente, no se puede bloquear en objetos arbitrarios en Delphi 6 (aunque puede hacerlo en versiones más recientes, 2009 y posteriores), entonces debe tener un objeto de bloqueo por separado, generalmente una sección crítica.

TCriticalSection (nota: la documentación es de FreePascal, pero existe en Delphi también): Código de

Ejemplo:

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 introdujo la posibilidad de obtener un bloqueo en cualquier objeto - "¿Por qué el tamaño del TObject se ha duplicado en Delphi 2009" ver en http://blogs.teamb.com/craigstuntz/2009/03/25/38138/ – mjn

+2

Sí, algo más tarde supe de eso, pero la pregunta ya había sido editada para mencionar Delphi 6, así que no me molesté en actualizar mi respuesta solo para devolverlo a la página principal. Debería haber dejado un comentario sin embargo. Pero, voy a hacer una nota para decir que me opongo a la opinión en ese artículo. La capacidad de bloquear cualquier objeto es * no * algo bueno, y puede llevar a bloqueos si no sabes lo que estás haciendo. Como cualquiera puede bloquear cualquier objeto, a veces lo hacen. Es mucho mejor asignar específicamente objetos de bloqueo y usarlos. Sin sorpresas (al respecto) –

0

Como se ha dicho, para el código corto, que no llame fuera el alcance local y no adquiere ningún otro bloqueo, puede usar secciones críticas a través de SyncObjs.TCriticalSection,
para código más largo/más complicado que puede usar SyncObjs.TMutex, que es waitable (con tiempo de espera), no se detiene si el hilo propietario muere y se puede compartir por nombre con otros procesos.
El uso de estas envolturas facilita los cambios en la capa de sincronización.

En todos los casos, se guarda de dragones aquí: my answer to Difference between the WaitFor function for TMutex delphi and the equivalent in win32 API

3

Aunque no del todo tan fácil como C#, después podría funcionar para usted.

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

En pocas palabras

  • una lista se mantiene para cada instancia que desea proteger.
  • cuando un segundo hilo llama al Lock(mySharedObj), se buscará en la lista interna un bloqueo existente. Se creará un nuevo bloqueo si no se encuentra ningún bloqueo existente. El nuevo hilo se bloqueará si otro hilo todavía tiene el bloqueo.
  • la Unlock es necesario porque no podemos estar seguros de que la referencia a la instancia iLock sólo salir del alcance al final del método llamando Lock. (Si pudiéramos, el Unlock podría eliminarse).

Tenga en cuenta que en este diseño, uno TLock se crea para cada instancia de objeto que desea proteger sin que se liberó hasta que la aplicación termina.
Esto podría tenerse en cuenta, pero implicaría perder el tiempo con _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 lista de Bloqueos también debería ser ordenada de alguna manera y la "localización" debe realizarse a través de una búsqueda binaria para mejorar el rendimiento. –

+0

@Ken Bourassa: Es cierto que queda mucho por mejorar. El objetivo, sin embargo, era mostrar cómo una construcción similar a la utilizada en C# podría realizarse con Delphi 6. –

11

No hay un equivalente en Delphi 6. A partir de Delphi 2009, puede utilizar los métodos System.TMonitor para agarrar las cerraduras de objetos arbitrarios.

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

(Se necesita el prefijo "Sistema", porque los conflictos de nombres TMonitor con el tipo de la unidad de las formas. La alternativa es el uso de los mundiales MonitorEnter y MonitorExit funciones.)

0

ayudantes de clase Uso de que puedas utilizar esta. Sin embargo, no funcionará con versiones anteriores. Pero recomendaría usar TMonitor solo en XE5. Dado que es bastante más lento 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; 
Cuestiones relacionadas