delphi TInterlocked.CompareExchange()是否支持接口对象?

093gszye  于 2023-03-29  发布在  其他
关注(0)|答案(1)|浏览(133)

我有一个懒惰的创建情况,一个(单例)对象只在需要它的代码执行时才被创建。这是在ISAPI dll的上下文中操作的,它是多线程的,所以存在两个线程可以访问它的风险。
我的代码最初看起来像这样,非常天真:

...
interface

var
  FHIRResourceFactory: function: IFHIRResourceFactory;

implementation
...
// global var and function
var
  _FHIRFactory: IFHIRResourceFactory;

function GetFHIRResourceFactory: IFHIRResourceFactory;
begin
  if (_FHIRFactory = nil) then
    { The object doesn't exist yet. Create one. }
    _FHIRFactory := TFHIRResourceFactory.Create;
  Result := _FHIRFactory;
end;

initialization

FHIRResourceFactory := GetFHIRResourceFactory;
end.

我重构了它以使用AtomicCmpExchange(),但我发现创建的对象福尔斯了范围:

...
interface

var
  FHIRResourceFactory: function: IFHIRResourceFactory;

implementation
...
// global var and function
var
  _FHIRFactory: IFHIRResourceFactory;

function GetFHIRResourceFactory: IFHIRResourceFactory;
var
  newFHIRFactory: IFHIRResourceFactory;
begin
  if (_FHIRFactory = nil) then
  begin
    { The object doesn't exist yet. Create one. }
    newFHIRFactory := TFHIRResourceFactory.Create;
    { It's possible another thread also created one.
      Only one of us will be able to set the _FHIRFactory singleton variable }
   if AtomicCmpExchange(Pointer(_FHIRFactory), Pointer(newFHIRFactory), nil) <>= nil then
    begin
      { The other beat us. Destroy our newly created object and use theirs. }
      newFHIRFactory := nil;
    end;
// if I don't add this line the object will fall out of scope and be destroyed
//      _FHIRFactory._AddRef
  end;
  Result := _FHIRFactory;
end;

initialization

FHIRResourceFactory := GetFHIRResourceFactory;
end.

是否有一个版本的TInterlocked.CompareExchange()可以接受接口对象?

goucqfw6

goucqfw61#

不幸的是,TInterlocked * 不 * 支持处理引用计数接口(奇怪的是,它的TObject方法 * 在ARC系统下处理引用计数对象)。因此,您必须手动管理接口引用计数。
例如,这是Indy使用的函数:

function InterlockedCompareExchangeIntf(var VTarget: IInterface; const AValue, Compare: IInterface): IInterface;
  {$IFDEF USE_INLINE}inline;{$ENDIF}
begin
  // TInterlocked does not have an overload for IInterface.
  // We have to ensure that the reference counts of the interfaces are managed correctly...
  if AValue <> nil then begin
    AValue._AddRef;
  end;
  Result := IInterface(InterlockedCompareExchangePtr(Pointer(VTarget), Pointer(AValue), Pointer(Compare)));
  if (AValue <> nil) and (Pointer(Result) <> Pointer(Compare)) then begin
    AValue._Release;
  end;
end;

(其中InterlockedCompareExchangePtr()是各种特定于平台的函数的 Package 器,包括可用的TInterlocked.CompareExchange()
您可以用已经使用的AtomicCmpExchange()TInterlocked.CompareExchange()Pointer重载替换InterlockedCompareExchangePtr()
例如:

function GetFHIRResourceFactory: IFHIRResourceFactory;
var
  newFHIRFactory: IFHIRResourceFactory;
begin
  if (_FHIRFactory = nil) then
  begin
    { The object doesn't exist yet. Create one. }
    newFHIRFactory := TFHIRResourceFactory.Create;
    { It's possible another thread also created one.
      Only one of us will be able to set the _FHIRFactory singleton variable }
    newFHIRFactory._AddRef;
    if (TInterlocked.CompareExchange(Pointer(_FHIRFactory), Pointer(newFHIRFactory), nil) <> nil then begin
      { The other beat us. Destroy our newly created object and use theirs. }
      newFHIRFactory._Release;
    end;
  end;
  Result := _FHIRFactory;
end;

如果_FHIRFactory被设置为新对象,则其recount将递增为2,然后在newFHIRFactory超出范围时递减为1,从而使对象保持活动状态。
如果_FHIRFactory没有被设置为新对象,它的recount将增加到2,然后返回到1,然后当newFHIRFactory超出范围时减少到0,从而销毁对象。

相关问题