Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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

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
Multithreading Delphi-是TDictionary线程安全的吗_Multithreading_Delphi_Tcp_Thread Safety_Indy - Fatal编程技术网

Multithreading Delphi-是TDictionary线程安全的吗

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.

我的想法是使用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;
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。。。这只是管理客户端连接的另一个想法。代码并不完整,只是为了理解。但是你回答了我需要知道的,关于字典是线程安全的。非常感谢。