2014-02-23 90 views
6

我已經構建了一個簡單的日誌類,並且想確認它是線程安全的。基本上Log,RegisterLoggerUnRegisterLogger將被從不同的線程調用。 Log將被稱爲很多(從許多不同的線程)和RegisterLoggerUnRegisterLogger很少。德爾福閱讀TList <x>線程安全嗎?

基本上我的問題可以歸結爲:「TList<x>線程安全嗎?」,也就是說我可以有多個線程在同一時間訪問TList

IExecutionCounterLogger是與登錄方法的接口(與相同的簽名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; 

作爲比特更多的背景,這是從一個this question跟隨上。基本上,我已經爲(DCOM)DataSnap服務器的每種方法添加了一些工具,我也已經將其添加到每個TDataSnapProvider OnGetData和OnUpdateData事件中。

回答

8

讀取TList<T>線程安全嗎?也就是說我可以有多個線程同時訪問TList<T>嗎?

這是線程安全的,不需要同步。多個線程可以安全地同時讀取。這相當於(並且實際上是)從數組中讀取數據。只有你的一個線程修改了需要同步的列表。

您的代碼比這種情況稍微複雜一些。你似乎需要迎合修改列表的線程。但你已經完成了TMultiReadExclusiveWriteSynchronizer這是一個非常好的解決方案。它允許多個讀取線程同時運行,但任何寫入線程都相對於所有其他線程被序列化。

2

強調問題的第一部分,您聲明對RegisterLogger和UnregisterLogger的調用很少。當日志調用只讀取列表時,其他兩個正在改變列表。在這種情況下,您必須確保在執行日誌調用或可能發生日誌調用時都不會執行這些操作。

想象一下UnregisterLogger中的刪除是在Log中的for循環期間執行的。至少結果是不可預測的。

僅在這兩個寫入調用中使用同步器是不夠的。

所以回答你的問題

是讀取從TList線程安全的?

只能是:這要看!

如果您可以確保沒有RegisterLogger和UnregisterLogger發生(即只能讀取呼叫),則可以安全地省略Synchronizer。否則 - 最好不要。

+2

同步器是TMultiReadExclusiveWriteSynchronizer –

+0

也許我沒有得到正確的問題。使用同步器顯然是線程安全的(多數民衆贊成在整個目的)。我明白阿利斯特,他希望省略閱讀部分。 –

+2

不,Alister詢問當多個「閱讀器」將同時運行時,TList 閱讀是否能夠正常工作。 – gabr