2014-02-23 6 views
6

간단한 로깅 클래스를 작성했으며 스레드로부터 안전한지 확인하고자합니다. 기본적으로 Log, RegisterLoggerUnRegisterLogger은 다른 스레드에서 호출됩니다. Log은 많은 스레드에서 많이 호출되고 RegisterLoggerUnRegisterLogger이라고 자주 호출됩니다.Delphi에서 TList <x> 스레드 안전성을 읽습니까?

기본적으로 내 질문은 다음과 같습니다. "TList<x>은 스레드 안전합니까?"즉, 복수 스레드가 동시에 TList에 액세스 할 수 있습니까?

IExecutionCounterLogger 이것이이 this question에서 계속 수행하고, (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; 

이다. 기본적으로 (DCOM) DataSnap 서버의 모든 메서드에 일부 계측을 추가했으며 모든 TDataSnapProvider OnGetData 및 OnUpdateData 이벤트에 연결했습니다.

답변

8

TList<T>은 스레드로부터 안전합니까? 즉, 복수 스레드가 TList<T>에 동시에 액세스 할 수 있습니까?

이는 스레드 안전하며 동기화가 필요하지 않습니다. 여러 스레드가 안전하게 동시에 읽을 수 있습니다. 이는 배열에서 읽는 것과 같고 실제로 배열에서 읽는 것과 같습니다. 스레드 중 하나가 동기화가 필요하다는 목록을 수정하는 경우에만 해당됩니다.

코드가이 시나리오보다 조금 복잡합니다. 목록을 수정하는 스레드를 처리해야하는 것처럼 보입니다. 그러나 완벽하게 좋은 해결책 인 TMultiReadExclusiveWriteSynchronizer으로 그렇게했습니다. 다중 읽기 스레드가 동시에 작동 할 수 있지만 쓰기 스레드는 다른 모든 스레드와 관련하여 직렬화됩니다.

2

질문의 첫 번째 부분을 강조하면 RegisterLogger 및 UnregisterLogger에 대한 호출이 자주 발생하지 않는다고 명시합니다. 로그 호출이 목록을 읽는 동안, 다른 두 사람은 목록을 변경합니다. 이 경우 로그 호출이 실행 중이거나 발생할 수있는 동안 이들 중 어느 것도 실행되지 않도록해야합니다.

UnregisterLogger의 삭제가 Log의 for 루프 중에 실행된다고 상상해보십시오. 그 결과는 적어도 예측할 수 없습니다.

Synchronizer는 두 번의 쓰기 호출에서만 사용하기에 충분하지 않습니다.

그래서 귀하의 질문에

있습니다에 대한 답은 안전 TList를 스레드에서 읽어?

다음과 같이 설정할 수 있습니다.

RegisterLogger 및 UnregisterLogger가 발생하지 않도록 (즉, 읽기 호출 만 수행 할 수 있도록) Synchronizer를 안전하게 생략 할 수 있습니다. 그렇지 않으면 - 더 좋지 않습니다.

+2

동기화 기는 TMultiReadExclusiveWriteSynchronizer –

+0

일 것입니다. Synchronizer를 사용하면 스레드 안전성이 보장됩니다 (전체 목적). 나는 앨리스터가 읽은 부분에 대해 그것을 생략하고 싶다고 이해했다. –

+2

아니요, Alister는 여러 개의 "리더"가 동시에 실행될 때 TList 읽기가 올바르게 작동하는지 묻고있었습니다. – gabr