multithreading delphi tcp thread-safety indy

multithreading - Delphi-¿Es seguro el hilo TDictionary?



tcp thread-safety (2)

Mi idea es usar TDictionary para administrar conexiones de clientes en IdTCPServer. Aquí hay un código de ejemplo simple (no probado) para fines de comprensión:

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;

¿Es seguro este hilo de código?


En una palabra: No.

Si inspecciona la fuente de TDictionary , debe darse cuenta rápidamente de que no hay ninguna disposición para la seguridad de subprocesos en la implementación en sí. Incluso si lo fuera, al tener llamadas discretas a una instancia de Dic , tiene condiciones potenciales de carrera con las que lidiar:

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;

Debes hacer que tu propio uso del thread Dic sea seguro, y afortunadamente en este tipo de ejemplo esto se logra fácilmente usando un monitor en el objeto mismo:

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;

Si no está familiarizado con los monitores en Delphi, en términos simples puede pensar en un monitor como una sección crítica lista para usar respaldada por cada descendiente de TObject (en versiones anteriores de Delphi que no admitían estos monitores, podría haber logrado el Lo mismo con una sección crítica explícita).


Para responder a su pregunta específica, no, TDictionary NO es seguro para subprocesos, por lo que debe proteger el acceso.

Su código no está manejando la posibilidad de que haya varios clientes detrás de un proxy / enrutador que se conecte al mismo servidor. Todos tendrán los mismos valores PeerIP y HostName . Esos valores no son lo suficientemente únicos por sí mismos para identificar a los clientes. Necesita crear sus propios identificadores únicos, por ejemplo, haciendo que sus clientes inicien sesión en su servidor con un nombre de usuario, y luego utilícelo como su clave de diccionario.

Y, por último, ¡NO TIdContext objetos TIdContext ! Son propiedad de TIdTCPServer y se liberarán automáticamente después de que el controlador de eventos OnDisconnect haya salido.