Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 使用带枚举器的锁定(TMonitor)安全吗';s构造函数/析构函数?_Delphi_Locking_Enumerator_Tmonitor - Fatal编程技术网

Delphi 使用带枚举器的锁定(TMonitor)安全吗';s构造函数/析构函数?

Delphi 使用带枚举器的锁定(TMonitor)安全吗';s构造函数/析构函数?,delphi,locking,enumerator,tmonitor,Delphi,Locking,Enumerator,Tmonitor,我有一个简单的线程安全容器类。它有标准的添加/删除方法。 通常,枚举项的实现方式如下: MyList.lock; try // looping here finally MyList.unlock; end; 但我想以线程安全的方式利用for来支持: for item in MyList do begin // do something end; 我的枚举器实现将容器锁定在其构造函数中,并在析构函数中解锁它。这是可行的,但前提是枚举器的实例是在for-in循环开始时创建的,并在

我有一个简单的线程安全容器类。它有标准的添加/删除方法。 通常,枚举项的实现方式如下:

MyList.lock;
try
  // looping here
finally
  MyList.unlock;
end;
但我想以线程安全的方式利用for来支持:

for item in MyList do 
begin
  // do something
end;
我的枚举器实现将容器锁定在其构造函数中,并在析构函数中解锁它。这是可行的,但前提是枚举器的实例是在for-in循环开始时创建的,并在循环结束时销毁。我在这里找到了这样的解释:

但由于锁定/解锁是一项关键操作,我想知道这种用法是否合适

以下是我的实现:

  TContainer<T> = class
    private
      FPadLock: TObject;
      FItems: TList<T>;
    protected
    public
      type
        TContainerEnumerator = class(TList<T>.TEnumerator)
          private
            FContainer: TContainer<T>;
          public
            constructor Create(AContainer: TContainer<T>);
            destructor Destroy; override;
        end;
      constructor Create;
      destructor Destroy; override;
      procedure add(AItem: T);
      procedure remove(AItem: T);
      function GetEnumerator: TContainerEnumerator;
  end;

{ TContainer<T> }

procedure TContainer<T>.add(AItem: T);
begin
  TMonitor.Enter(FPadLock);
  try
    FItems.Add(AItem);
  finally
    TMonitor.Exit(FPadLock);
  end;
end;

constructor TContainer<T>.Create;
begin
  inherited Create;
  FPadLock := TObject.Create;
  FItems := TList<T>.Create;
end;

destructor TContainer<T>.Destroy;
begin
  FreeAndNil(FItems);
  FreeAndNil(FPadLock);
  inherited;
end;

procedure TContainer<T>.remove(AItem: T);
begin
  TMonitor.Enter(FPadLock);
  try
    FItems.Remove(AItem);
  finally
    TMonitor.Exit(FPadLock);
  end;
end;

function TContainer<T>.GetEnumerator: TContainerEnumerator;
begin
  result := TContainerEnumerator.Create(self);
end;

{ TContainer<T>.TContainerEnumerator }

constructor TContainer<T>.TContainerEnumerator.Create(
  AContainer: TContainer<T>);
begin
  inherited Create(AContainer.FItems);
  FContainer := AContainer;
  TMonitor.Enter(FContainer.FPadLock);  // <<< Lock parent container using Monitor
end;

destructor TContainer<T>.TContainerEnumerator.Destroy;
begin
  TMonitor.Exit(FContainer.FPadLock);  // <<< Unlock parent container
  inherited;
end;
t容器=类
私有的
FPadLock:TObject;
FItems:TList;
受保护的
公众的
类型
TContainerEnumerator=类(TList.TEnumerator)
私有的
f容器:t容器;
公众的
构造函数创建(容器:TContainer);
毁灭者毁灭;推翻
结束;
构造函数创建;
毁灭者毁灭;推翻
程序添加(AItem:T);
程序删除(AItem:T);
函数GetEnumerator:TContainerEnumerator;
结束;
{t容器}
程序TContainer.add(AItem:T);
开始
t监控输入(FPadLock);
尝试
FItems.Add(AItem);
最后
t监控出口(FPadLock);
结束;
结束;
构造函数TContainer.Create;
开始
继承创造;
FPadLock:=TObject.Create;
FItems:=TList.Create;
结束;
销毁器t容器。销毁;
开始
FreeAndNil(FItems);
FreeAndNil(FPadLock);
继承;
结束;
程序T容器。移除(AItem:T);
开始
t监控输入(FPadLock);
尝试
FItems.Remove(AItem);
最后
t监控出口(FPadLock);
结束;
结束;
函数TContainer.GetEnumerator:TContainerEnumerator;
开始
结果:=TContainerEnumerator.Create(self);
结束;
{TContainer.TContainerEnumerator}
构造函数TContainer.TContainerEnumerator.Create(
容器:t容器);
开始
继承的Create(AContainer.FItems);
F容器:=容器;

TMonitor.Enter(FContainer.FPadLock);// 枚举数在for循环开始时创建,在循环结束时销毁。枚举数的生存期由try/finally管理

不过,不要只是相信我的话。添加一些调试代码很容易,这些代码将检测循环,并让您看到何时调用析构函数

这意味着您建议的锁定策略是合理的


我想说的是,当存在线程争用时,在堆上分配枚举器可能会导致性能问题

如果您关心perf,尤其是多线程环境中的perf,则不会在heap@DavidHeffernan:我只想确保在退出for in-loop后,初始锁后面会有匹配的解锁。@DavidHeffernan:例如,如果for的正文中发生异常,我需要确保在所有情况下都调用了解锁。只是好奇:枚举数不是总是在堆上分配的吗?或者你的意思是使用枚举数通常会导致性能问题?如果你的枚举数是记录,它是堆栈分配的。我的都是。但是你不能用你的锁。记录没有析构函数。