Delphi - Is TDictionary thread safe

烈酒焚心 提交于 2019-12-07 09:13:43

问题


My idea is to use TDictionary to manage client connections on 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?


回答1:


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. Even if it were, by having discrete calls to a Dic instance you have potential race conditions to contend with:

  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:

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).




回答2:


To answer your specific question - no, TDictionary is NOT thread-safe, so you must protect access to it.

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. 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! They are owned by TIdTCPServer and will be freed automatically after the OnDisconnect event handler has exited.



来源:https://stackoverflow.com/questions/27517063/delphi-is-tdictionary-thread-safe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!