Ведение журнала и синхронизация - PullRequest
3 голосов
/ 18 марта 2009

Я только что написал свой собственный каркас логирования (очень легкий, нет необходимости в большом каркасе логирования). Он состоит из интерфейса ILogger и ряда классов, реализующих этот интерфейс. Вопрос, о котором у меня есть вопрос, - это TGUILogger, который принимает TStrings в качестве цели ведения журнала и синхронизирует запись в журнал с основным потоком, так что элемент Items списка может использоваться в качестве цели.

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

Что я здесь не так делаю?

РЕДАКТИРОВАТЬ: я только что нашел следующую подсказку в справке Delphi для TThread.StaticSynchronize

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

Это может быть именно моей проблемой, так как некоторые запросы на регистрацию поступают из основного потока. Как я могу решить это?

Ответы [ 2 ]

7 голосов
/ 18 марта 2009

Если вы сравниваете CurrentThreadID с MainThreadID, вы можете выбрать синхронизацию или нет.

Лично я предпочел, чтобы графический интерфейс запрашивал у системы журналов последнюю информацию, а не прерывал потоки. В противном случае ваше ведение журнала мешает быстрой работе потока, что лишает его цели.

2 голосов
/ 18 марта 2009

Если вы не нашли более простого способа, попробуйте сделать следующее:

При инициализации программы (из основного потока) ваша подсистема ведения журнала вызывает функцию API Windows GetCurrentThreadID и сохраняет результат в переменной. (EDIT: переменная MainThreadID в системном блоке автоматически инициализируется таким образом для вас при запуске. Спасибо, mghie.) Когда после этого поступает запрос на регистрацию, снова вызовите GetCurrentThreadID и синхронизируйте, только если он поступает из другого потока.

Существуют и другие приемы, не связанные с Windows API, но в итоге они становятся более сложными, особенно если у вас есть несколько различных пользовательских потомков TThread. Однако основной принцип тот же: проверьте, находитесь ли вы в главном потоке, прежде чем решить, вызывать или нет StaticSynchronize.

...