2014-02-23 4 views
6

Ich habe eine einfache Protokollierungsklasse erstellt und möchte bestätigen, dass sie Thread-sicher ist. Grundsätzlich werden die Log, RegisterLogger und UnRegisterLogger aus verschiedenen Threads aufgerufen. Log wird viel (von vielen verschiedenen Threads) und RegisterLogger und UnRegisterLogger selten genannt werden.In Delphi sind auf TList <x> Thread sicher gelesen?

Grundsätzlich kann meine Frage auf den Punkt gebracht werden: "Sind auf TList<x> thread sicher gelesen?", Das heißt, kann ich mehrere Threads Zugriff auf eine TList zur gleichen Zeit haben.

IExecutionCounterLogger ist eine Schnittstelle mit einem Log-Methode (mit derselben Signatur wie TExecutionCounterServer.Log)

Type 
    TExecutionCounterServer = class 
    private 
    Loggers : TList<IExecutionCounterLogger>; 
    Synchronizer : TMultiReadExclusiveWriteSynchronizer; 
    public 
    procedure RegisterLogger(Logger : IExecutionCounterLogger); 
    procedure UnRegisterLogger(Logger : IExecutionCounterLogger); 
    procedure Log(const ClassName, MethodName : string; ExecutionTime_ms : integer); 

    constructor Create; 
    destructor Destroy; override; 
    end; 

constructor TExecutionCounterServer.Create; 
begin 
    Loggers := TList<IExecutionCounterLogger>.Create; 
    Synchronizer := TMultiReadExclusiveWriteSynchronizer.Create; 
end; 

destructor TExecutionCounterServer.Destroy; 
begin 
    Loggers.Free; 
    Synchronizer.Free; 
    inherited; 
end; 

procedure TExecutionCounterServer.Log(const ClassName, MethodName: string; ExecutionTime_ms: integer); 
var 
    Logger: IExecutionCounterLogger; 
begin 
    Synchronizer.BeginRead; 
    try 
    for Logger in Loggers do 
     Logger.Log(ClassName, MethodName, ExecutionTime_ms); 
    finally 
    Synchronizer.EndRead; 
    end; 
end; 

procedure TExecutionCounterServer.RegisterLogger(Logger: IExecutionCounterLogger); 
begin 
    Synchronizer.BeginWrite; 
    try 
    Loggers.Add(Logger); 
    finally 
    Synchronizer.EndWrite; 
    end; 
end; 

procedure TExecutionCounterServer.UnRegisterLogger(Logger: IExecutionCounterLogger); 
var 
    i : integer; 
begin 
    Synchronizer.BeginWrite; 
    try 
    i := Loggers.IndexOf(Logger); 
    if i = -1 then 
     raise Exception.Create('Logger not present'); 
    Loggers.Delete(i); 
    finally 
    Synchronizer.EndWrite; 
    end; 
end; 

Als wenig Hintergrund, das ein aus this question folgen eingeschaltet ist. Im Grunde habe ich zu jeder Methode eines (DCOM) DataSnap-Servers eine Instrumentierung hinzugefügt, außerdem bin ich in jedes TDataSnapProvider OnGetData und OnUpdateData-Ereignis eingebunden.

Antwort

8

Sind liest auf TList<T> thread sicher? Das heißt, kann ich mehrere Threads haben, die gleichzeitig auf TList<T> zugreifen?

Das ist Thread-Safe und benötigt keine Synchronisation. Mehrere Threads können gleichzeitig sicher lesen. Das entspricht (und wird tatsächlich implementiert) dem Lesen von einem Array. Nur wenn einer Ihrer Threads die Liste ändert, ist eine Synchronisierung erforderlich.

Ihr Code ist ein wenig komplexer als dieses Szenario. Sie müssen anscheinend auf Threads achten, die die Liste modifizieren. Aber Sie haben es mit TMultiReadExclusiveWriteSynchronizer getan, was eine perfekte Lösung ist. Es ermöglicht mehrere Lese-Threads, gleichzeitig zu arbeiten, aber alle Schreib-Threads sind in Bezug auf alle anderen Threads serialisiert.

2

Unter Hervorhebung des ersten Teils Ihrer Frage geben Sie an, dass Anrufe zu RegisterLogger und UnregisterLogger selten sind. Während der Protokollaufruf nur die Liste liest, ändern diese beiden anderen die Liste. In diesem Fall müssen Sie sicherstellen, dass keine von diesen ausgeführt wird, während ein Protokollaufruf ausgeführt wird oder auftreten kann.

Stellen Sie sich ein Löschen in UnregisterLogger wird während der for-Schleife in Log ausgeführt. Die Ergebnisse sind mindestens unvorhersehbar.

Es wird nicht ausreichen, den Synchronizer nur in diesen beiden Schreibaufrufen zu verwenden.

So ist die Antwort auf Ihre Frage

Are liest auf TList Thread-sicher?

kann nur sein: es kommt darauf an!

Wenn Sie sicherstellen können, dass RegisterLogger und UnregisterLogger nicht passieren (d. H. Nur Leseaufrufe können vorkommen), können Sie den Synchronizer sicher weglassen. Sonst - besser nicht.

+2

Der Synchronisierer ist TMultiReadExclusiveWriteSynchronizer –

+0

Vielleicht habe ich die Frage nicht richtig. Die Verwendung des Synchronizers ist offensichtlich threadsicher (das ist der ganze Zweck). Ich verstand Alister, dass er es für den Leseteil weglassen wollte. –

+2

Nein, Alister fragt, ob TList Reads ordnungsgemäß funktionieren, wenn mehrere "Reader" gleichzeitig ausgeführt werden. – gabr