2014-02-23 47 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