Multithreading Delphi-是TDictionary线程安全的吗
我的想法是使用TDictionary来管理IdTCPServer上的客户端连接。以下是一个简单的示例代码(未测试),以便于理解:Multithreading Delphi-是TDictionary线程安全的吗,multithreading,delphi,tcp,thread-safety,indy,Multithreading,Delphi,Tcp,Thread Safety,Indy,我的想法是使用TDictionary来管理IdTCPServer上的客户端连接。以下是一个简单的示例代码(未测试),以便于理解: var Dic: TDictionary<string, TIdContext>; procedure TfrmMain.FormCreate(Sender: TObject); begin Dic := TDictionary<string, TIdContext>.Create; end; procedure TfrmMain.
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;
var
Dic:t词典;
过程TfrmMain.FormCreate(发送方:TObject);
开始
Dic:=TDictionary.Create;
结束;
程序TfrmMain.FormDestroy(发送方:ToObject);
开始
免费;
结束;
过程TfrmMain.TCPServerConnect(AContext:TIdContext);
变量
主机名:字符串;
开始
主机名:=大写(GStack.HostByAddress(AContext.Binding.PeerIP));
如果不是Dic.ContainsKey(主机名),则是Dic.Add(主机名,AContext);
结束;
过程TfrmMain.TCPServerDisconnect(AContext:TIdContext);
变量
主机名:字符串;
开始
主机名:=大写(GStack.HostByAddress(AContext.Binding.PeerIP));
如果Dic.ContainsKey(主机名),则
开始
Dic[Hostname]。免费;
删除(主机名);
结束;
结束;
这段代码是线程安全的吗?一句话:否 如果检查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旧版本中,您可以通过显式关键部分实现相同的功能).要回答您的具体问题-否,
t字典
不是线程安全的,因此您必须保护对它的访问
您的代码没有处理在代理/路由器后面连接到同一服务器的多个客户端的可能性。它们都将具有相同的PeerIP
和HostName
值。这些值本身不够独特,无法识别客户。您需要创建自己的唯一标识符,例如,让客户端使用用户名登录到服务器,然后将其用作字典密钥
最后,不要释放
TIdContext
对象!它们归TIdTCPServer
所有,并将在OnDisconnect
事件处理程序退出后自动释放。您确定要销毁上下文吗?如果是这样,你确定你不会泄露一些吗?@DavidHeffernan,我理解你的意思,但我不想测试这个代码。这只是为了理解的目的。。。谢谢作为经验法则:RTL、VCL、FMX的每个类都不是线程安全的,如果是,您将看到它在查看以tthread…
开头的类名时,会看到它。@SirRufo,非常好的提示!非常感谢。远比这复杂。没有一件事叫做线程安全。如果从不更改字典,则字典是线程安全的。使用TObjectDictionary
和Remove
删除字典时效率更高。当1个足够时,避免3次查找。当然,这假设了释放上下文是正确的,而事实并非如此。一旦Free
被删除,只需调用Remove
即可+1Ok@Remy Lebeau。。。这只是管理客户端连接的另一个想法。代码并不完整,只是为了理解。但是你回答了我需要知道的,关于字典是线程安全的。非常感谢。