Delphi - TDictionary线程是否安全

Guy*_*ush 4 delphi multithreading tcp indy thread-safety

我的想法是使用TDictionary来管理IdTCPServer上的客户端连接.这是一个简单的示例代码(未经过测试),用于理解目的:

var
  Dic: TDictionary<string, TIdContext>;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  Dic := TDictionary<string, TIdContext>.Create;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  Dic.Free;
end;

procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
  Hostname: string;
begin
  Hostname := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
  if not Dic.ContainsKey(Hostname) then Dic.Add(Hostname, AContext);
end;

procedure TfrmMain.TCPServerDisconnect(AContext: TIdContext);
var
  Hostname: string;
begin
  Hostname := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
  if Dic.ContainsKey(Hostname) then
  begin
    Dic[Hostname].Free;
    Dic.Remove(Hostname);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

这段代码线程安全吗?

Del*_*ics 7

总之一句: .

如果您检查TDictionary的源代码,您应该很快意识到实现本身没有提供线程安全性.即使它是,通过对Dic实例进行离散调用,您仍有潜在的竞争条件可以应对:

  if Dic.ContainsKey(Hostname) then
  begin

    // In theory the Hostname key may be removed by another thread before you 
    //  get a chance to do this : ...

    Dic[Hostname].Free;
    Dic.Remove(Hostname);
  end;
Run Code Online (Sandbox Code Playgroud)

您需要自己使用Dic线程安全,幸运的是在这种示例中,使用对象本身的监视器很容易实现:

MonitorEnter(Dic);
try
  if not Dic.ContainsKey(Hostname) then 
    Dic.Add(Hostname, AContext);

finally
  MonitorExit(Dic);
end;


// ....


MonitorEnter(Dic);
try
  if Dic.ContainsKey(Hostname) then
  begin
    Dic[Hostname].Free;
    Dic.Remove(Hostname);
  end;

finally
  MonitorExit(Dic);
end;
Run Code Online (Sandbox Code Playgroud)

如果您不熟悉Delphi中的监视器,简单来说,您可以将监视器视为每个TObject后代支持的即用型临界区(在旧版本的Delphi中,不支持这些监视器,您可以实现明确的关键部分同样的事情).