2010-06-11 35 views
13

我正在Delphi中编写一个多线程应用程序,需要使用一些东西来保护共享资源。德尔福 - 是否有任何相当于C#锁?

在C#中我会使用“锁定”的文章:

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

在Delphi中我找不到类似的话,我发现只是TThread.Synchronize(的someMethod)方法,它通过调用避免潜在冲突在的someMethod主VCL线程,但它不正是我想要做的....

编辑:我用Delphi 6

+1

至少可以调用Windows API的关键部分 – Arseny 2010-06-11 13:16:34

回答

17

(联合国)幸好你不能任意对象在Delphi 6锁定(尽管你可以在更新的版本,2009年及以后),所以你需要有一个单独的锁定对象,通常是关键部分。

TCriticalSection(注:文档从FreePascal的,但它在Delphi中也存在):

示例代码:

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

德尔福2009年推出,以获得任何对象的锁的能力 - “?为什么TObject中的规模扩大了一倍。2009年德尔福”看在http://blogs.teamb.com/craigstuntz/2009/03/25/38138/ – mjn 2010-11-21 16:22:27

+2

是的,我了解了一段时间后,但问题已编辑提到德尔福6,所以我没有打扰更新我的答案只是为了让它回到首页。尽管我应该留下评论。但是,我会做一个说明,说我反对该文的观点。锁定任何对象的能力不是一件好事,如果你不知道自己在做什么,很容易导致死锁。由于任何人都可以锁定任何对象,有时他们会这样做。专门分配锁定对象并使用这些对象要好得多。没有惊喜(在这方面)。 – 2010-11-23 08:57:44

0

如所述,对于短码,这不调用外部本地范围并且没有获取任何其他锁,您可以通过SyncObjs.TCriticalSection
使用关键部分来获得更长/更复杂的代码,您可以使用SyncObjs.TMutex,这是可等待的(超时),如果拥有的线程死掉并可以与其他进程共享名称。
使用这些包装可以更轻松地更改同步层。

在所有情况下,提防龙的位置:my answer to Difference between the WaitFor function for TMutex delphi and the equivalent in win32 API

3

虽然不完全一样容易C#,以下可能为你工作。

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

简而言之

  • 一个列表保存为每例如要保护。
  • 当第二个线程收到Lock(mySharedObj)时,将搜索内部列表以查找现有的锁。如果没有找到现有的锁,将会创建一个新的锁。如果另一个线程仍然有锁,新线程将被阻止。
  • Unlock是必要的,因为我们不能确定对ILock实例的引用仅在调用Lock的方法的末尾处超出范围。 (如果可以,可以删除Unlock)。

注意,在这个设计,一个TLOCK获取要保护它不被释放,直到应用程序终止每一个对象实例创建的。
这可能是因素考虑因素,但会涉及到_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

锁定列表还需要以某种方式排序,并通过二进制搜索来完成“定位”以提高性能。 – 2010-06-11 16:55:35

+0

@Ken Bourassa:没错,还有很大的改进余地。尽管目的只是为了展示如何用Delphi 6完成类似于C#中使用的构造。 – 2010-06-11 18:29:33

11

有德尔福6没有等价截至2009年德尔福,你可以使用System.TMonitor方法去抓任意对象的锁。

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

(你还需要“系统”前缀,因为在窗体单元的类型TMonitor名称冲突。另一种方法是使用全局MonitorEnterMonitorExit功能。)

0

使用类的帮手,你可以用这个。不过,旧版本不能使用。但我会建议仅在XE5中使用TMonitor。因为它比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;