I've built a simple logging class and want to confirm that it is thread safe. Basically the Log
, RegisterLogger
and UnRegisterLogger
will be called from different threads. Log
will be called alot (from many different threads) and RegisterLogger
and UnRegisterLogger
infrequently.
Basically my question can be boiled down to is: "Are reads on TList<x>
thread safe?", that is to say can I have multiple threads accessing a TList
at the same time.
IExecutionCounterLogger
is an interface with a Log method (with the same signature as 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;
As a bit more background, this is a follow on from this question. Basically I've added some instrumentation to every method of a (DCOM) DataSnap server, also I've hooked into every TDataSnapProvider OnGetData and OnUpdateData event.
A method will be thread safe if it uses the synchronized keyword in its declaration.
As long as it's a plain variable, it's no risk. If it is a property, reading it can possibly have side effects, so is not guaranteed to be thread safe. Save this answer.
To use a thread object in your application (and to create a descendant of Classes. TThread): Choose one: File > New > Other > Delphi Projects > Delphi Files > Thread Object.
Answer: Since String is immutable in Java, it's inherently thread-safe.
Are reads on
TList<T>
thread safe? That is to say can I have multiple threads accessing aTList<T>
at the same time?
That is thread safe and needs no synchronisation. Multiple threads can safely read concurrently. That is equivalent to (and in fact implemented as) reading from an array. It is only if one of your threads modifies the list that synchronisation is needed.
Your code is a little more complex than this scenario. You do appear to need to cater for threads modifying the list. But you've done so with TMultiReadExclusiveWriteSynchronizer
which is a perfectly good solution. It allows multiple reads threads to operate concurrently, but any write threads are serialized with respect to all other threads.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With