2009-03-18 3 views
3

방금 ​​내 자체 로깅 프레임 워크를 작성했습니다 (매우 가볍고 큰 로깅 프레임 워크 필요 없음). 인터페이스 ILogger와 그 인터페이스를 구현하는 여러 클래스로 구성됩니다. 하나의 질문에 대한 TGUILogger TStrings 로깅 대상으로 걸리고 목록 상자의 항목 멤버를 대상으로 사용할 수 있도록 주 스레드 로깅을 동기화합니다.로깅 및 동기화

type 
    ILogger = Interface (IInterface) 
    procedure Log (const LogString : String; LogLevel : TLogLevel); 
    procedure SetLoggingLevel (LogLevel : TLogLevel); 
    end; 

type  
    TGUILogger = class (TInterfacedObject, ILogger) 
    public 
    constructor Create (Target : TStrings); 
    procedure Log (const LogString : String; LogLevel : TLogLevel); 
    procedure SetLoggingLevel (LogLevel : TLogLevel); 
    private 
    procedure PerformLogging; 
    end; 

procedure TGUILogger.Log (const LogString : String; LogLevel : TLogLevel); 
begin 
    TMonitor.Enter (Self); 
    try 
    FLogString := GetDateTimeString + ' ' + LogString; 
    TThread.Synchronize (TThread.CurrentThread, PerformLogging); 
    finally 
    TMonitor.Exit (Self); 
    end; 
end; 

procedure TGUILogger.PerformLogging; 
begin 
    FTarget.Add (FLogString); 
end; 

로깅이 작동하지만 응용 프로그램이 제대로 닫히지 않습니다. 클래스 유닛에 매달려있는 것 같습니다. 스택 추적 :

System.Halt0, System.FinalizeUnits, Classes.Finalization, Classes.FreeExternalThreads, System.TObject.Free, Classes.TThread.Destroy, Classes.TThread.RemoveQueuedEvents

내가 뭘 여기서 뭐라구?

편집 : 난 그냥이 메인 스레드에서 온 일부 로깅 요청 이후 정확히 내 문제가 될 수 TThread.StaticSynchronize

에 대한
Warning: Do not call StaticSynchronize from within the main thread. This can cause 
an infinite loop.  

를 델파이 도움말의 다음 힌트를 발견했다. 이 문제를 어떻게 해결할 수 있습니까?

프로그램 초기화에서

, 로깅 하위 시스템 호출 Windows API 함수 GetCurrentThreadID을 가지고 (주 스레드에서)에 결과를 저장 : 당신이 어떤 간단한 방법을 찾을 수없는 경우, 당신은이 일을 시도 할 수

+0

필자는 주제에 답하고 싶지 않지만, 정말로 이것을 사용하여 가벼운 무게가 필요하면 다시 디자인하거나 상용 라이브러리를 사용하십시오. 이 코드는 많은 문제점을 가지고 있으며 가벼운 무게는 아닙니다. mj2008의 답은 올바른 방향으로의 첫 번째 전환점입니다. – mghie

+0

재미 있군요, 그냥 오늘은 내 자신의 로깅 프레임 워크 (결국 실제 로깅을 할 SmartInspect를 사용하는)와 비슷한 것을 추가했습니다 .... – dummzeuch

+0

당신은 무엇을 추가 했습니까? TGUILogger.Log()를 호출하여 교착 상태가 발생할 가능성이 없습니까? – mghie

답변

2

변수. (수정 : 시스템 단위 MainThreadID 변수를 자동으로 시작될 때이 방식으로 초기화됩니다. 감사, mghie.) 이후 로깅 요청이 들어 오면 GetCurrentThreadID를 다시 호출하고 다른 스레드에서 오는 경우에만 동기화하십시오.

Windows API를 포함하지 않는 다른 트릭이 있습니다. 특히 사용자 정의 TThread 자손이 많이있는 경우 더욱 복잡해집니다. 기본 원칙은 동일합니다. StaticSynchronize를 호출할지 여부를 결정하기 전에 주 스레드에 있는지 확인합니다.

+0

MainThreadId 및 TThread.ThreadId를 사용하는 것이 잘못된 이유는 무엇입니까? – mghie

+0

아, 그건 "더 간단한 방법"입니다. 주 스레드의 ID를 가져 오는 기본 제공 방법이 있어야한다고 생각했지만 MainThreadID에 대해 알지 못했습니다. 내 대답을 편집 할게. TThread.ThreadID 문제는 현재 스레드에 대한 참조가 필요하다는 것입니다. –

+0

아, 그래, 그건 사실 문제입니다. 그러나 스레드 ID 값은 Delphi RTL에 IsMainThread() 함수 또는 이와 유사한 함수가 없기 때문에 여기서 비교를 위해서만 필요합니다. 13 년 전에도 유용한 추가 기능이 있었을까요? 델파이 2? – mghie

7

CurrentThreadID를 MainThreadID와 비교하면 동기화할지 여부를 선택할 수 있습니다.

필자는 개인적으로 GUI가 로그 정보 시스템에 최신 정보를 묻는 대신 스레드 일시 중지를 요청했습니다. 그렇지 않으면 로깅이 스레드의 빠른 작동을 방해하여 목적을 달성하지 못하게됩니다.

+0

+1, 불행히도 한 번 이상 투표 할 수 없습니다. 두 단락 모두 최대 득표, 두 번째 단락은 둘 이상이 될 자격이있다. 로깅 프레임 워크에서 원하지 않는 것은 속도 저하, 스레드 직렬화 및 강제 컨텍스트 전환입니다. 동기화()를 사용하면 세 가지가 모두 제공됩니다. 이 얼마나 악몽인가. – mghie

+1

+1 좋은 답변입니다. 당신의 두 번째 요지와 내가했던 방식대로 구현 한 이유 : GUI를 변경하지 않고 GUI 로거, 파일 로거, 이메일 로거 등을 가질 수 있도록 모든 로깅을 한 곳에서 원했습니다. 디자인이 깨끗하고 쉽게 변경할 수 있다면 나는 큰 성과없이 살 수 있습니다. – jpfollenius