我正在Delphi中编写一个多线程应用程序,需要使用一些东西来保护共享资源。
在C#中,我使用“lock”关键字:
private someMethod() {
lock(mySharedObj) {
//...do something with mySharedObj
}
}
在Delphi中我找不到类似的东西,我发现只有TThread.Synchronize(someMethod)方法,它通过在主VCL线程中调用someMethod来防止潜在的冲突,但这不是我想做的......
编辑:我正在使用Delphi 6
答案 0 :(得分: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 :(得分:11)
在Delphi 6中没有等价物。从Delphi 2009开始,您可以使用System.TMonitor
方法来获取任意对象的锁定。
System.TMonitor.Enter(obj);
try
// ...
finally
System.TMonitor.Exit(obj);
end;
(您需要“System”前缀,因为TMonitor名称与Forms单元中的类型冲突。另一种方法是使用全局MonitorEnter
和MonitorExit
函数。)
答案 2 :(得分: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.
答案 3 :(得分:0)
如上所述,对于不在本地范围之外调用且不获取任何其他锁定的短代码,您可以通过SyncObjs.TCriticalSection
使用关键部分,
对于更长/更复杂的代码,您可以使用SyncObjs.TMutex
,它是等待的(具有超时),如果拥有的线程死亡并且可以通过其他进程的名称共享,则不会停止。
使用这些包装器可以更轻松地更改同步层。
在所有情况下,请注意龙:my answer to Difference between the WaitFor function for TMutex delphi and the equivalent in win32 API
答案 4 :(得分: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;