[英]Delphi read-only generics like TDictionary<T> and TList<T> are thread safe?
[英]Delphi - Is TDictionary thread safe
我的想法是使用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;
这段代码线程安全吗?
总之一句: 不 。
如果您检查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;
您需要自己使用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;
如果您不熟悉Delphi中的监视器,简单来说,您可以将监视器视为每个TObject后代支持的即用型临界区(在旧版本的Delphi中,不支持这些监视器,您可以实现明确的关键部分同样的事情)。
要回答您的具体问题 - 不, TDictionary
不是线程安全的,因此您必须保护对它的访问。
您的代码不处理连接到同一服务器的代理/路由器后面的多个客户端的可能性。 它们都具有相同的PeerIP
和HostName
值。 这些值本身并不足以识别客户。 您需要创建自己的唯一标识符,例如让您的客户端使用用户名登录到您的服务器,然后将其用作您的字典密钥。
最后,不要释放TIdContext
对象! 它们归TIdTCPServer
所有,在OnDisconnect
事件处理程序退出后将自动释放。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.