简体   繁体   English

Delphi - TDictionary线程是否安全

[英]Delphi - Is TDictionary thread safe

My idea is to use TDictionary to manage client connections on IdTCPServer. 我的想法是使用TDictionary来管理IdTCPServer上的客户端连接。 Here is a simple example code (not tested) for understanding purposes: 这是一个简单的示例代码(未经过测试),用于理解目的:

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;

Is this code thread safe? 这段代码线程安全吗?

In a word: No . 总之一句:

If you inspect the source of TDictionary you should quickly realise that there is no provision for thread-safety in the implementation itself. 如果您检查TDictionary的源代码,您应该很快意识到实现本身没有提供线程安全性。 Even if it were, by having discrete calls to a Dic instance you have potential race conditions to contend with: 即使它是,通过对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;

You need to make your own use of Dic thread safe, and fortunately in this sort of example this is easily achieved using a monitor on the object itself: 您需要自己使用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;

If you are not familiar with monitors in Delphi, in simple terms you can think of a monitor as a ready-to-use critical section supported by every TObject descendant (in older versions of Delphi which did not support these monitors you could have achieved the same thing with an explicit critical section). 如果您不熟悉Delphi中的监视器,简单来说,您可以将监视器视为每个TObject后代支持的即用型临界区(在旧版本的Delphi中,不支持这些监视器,您可以实现明确的关键部分同样的事情)。

To answer your specific question - no, TDictionary is NOT thread-safe, so you must protect access to it. 要回答您的具体问题 - 不, TDictionary不是线程安全的,因此您必须保护对它的访问。

Your code is not handling the possibility of multiple clients behind a proxy/router connecting to the same server. 您的代码不处理连接到同一服务器的代理/路由器后面的多个客户端的可能性。 They will all have the same PeerIP and HostName values. 它们都具有相同的PeerIPHostName值。 Those values are not unique enough by themselves to identify clients. 这些值本身并不足以识别客户。 You need to create your own unique identifiers, for instance by having your clients login to your server with a username, and then use that as your dictionary key instead. 您需要创建自己的唯一标识符,例如让您的客户端使用用户名登录到您的服务器,然后将其用作您的字典密钥。

And lastly, DO NOT free TIdContext objects! 最后,不要释放TIdContext对象! They are owned by TIdTCPServer and will be freed automatically after the OnDisconnect event handler has exited. 它们归TIdTCPServer所有,在OnDisconnect事件处理程序退出后将自动释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM