How to implement a process queue in Delphi 6?

4
How to implement a process queue in Delphi 6 as the TThread.Queue of the newer versions?

What I need is to implement a queue for logging with Delphi 6. In newer versions I have the ability to use:

procedure TFormClient.QueueLogMsg(const s: string);
begin
  TThread.Queue(nil,
    procedure
    begin
      LogMsg(s)
    end
  );
end;

I've been thinking about creating a thread that would do this, but I do not know if I'll get errors when trying to add a new process to the process list while the thread is running.

What I thought:

TQueueLog = class(TThread)
private
  FLog: TStringList;
  FFile: TStringList;
  FFileName: string;
public
  constructor Create; reintroduce;
  destructor Destroy; override;
  procedure AddLog(value: string);
  procedure Execute; override;
end;
...

constructor TQueueLog.Create;
begin
  inherited Create(false);
  FreeOnTerminate := false;
  FLog := TStringList.Create;
  FFile := TStringList.Create;
  FFileName := 'log.txt';
end;

destructor TQueueLog.Destroy;
begin
  FLog.Free;
  FFile.Free;
  inherited;
end;

And the methods AddLog and Execute :

procedure TQueueLog.AddLog(value: string);
begin
  FLog.Add(value);
end;

procedure TQueueLog.Execute;
var
  count: integer;
begin
  while (not Self.Terminated) do
  begin
    if (FLog.Count > 0) then
    begin
      FFile.LoadFromFile(FFileName);
      for count := 0 to pred(FLog.Count) do
      begin
        FFile.Add(FLog.Strings[count]);
      end;
      FFile.SaveToFile(FFileName);
      FLog.Clear;
    end;
  end;
end;

Then, How to prevent access violation when trying to add an item to FLog by AddLog when the thread is running? Is there another correct way to implement this?

    
asked by anonymous 25.06.2014 / 15:53

1 answer

4

To prevent errors in this type of operation you should control the competition using, for example a critical section.

For this you will need an object of type TCriticalSection

TQueueLog = class(TThread)
private
  FLog: TStringList;
  FFile: TStringList;
  FFileName: string;
  FCriticalSection: TCriticalSection;

  function GetLogs: string;
public
  constructor Create; reintroduce;
  destructor Destroy; override;
  procedure AddLog(value: string);
  procedure Execute; override;
end;
...

constructor TQueueLog.Create;
begin
  inherited Create(false);
  FreeOnTerminate := false;
  FLog := TStringList.Create;
  FFile := TStringList.Create;
  FFileName := 'log.txt';
  FCriticalSection := TCriticalSection.Create;
end;

destructor TQueueLog.Destroy;
begin
  FLog.Free;
  FFile.Free;
  FCriticalSection.Free;
  inherited;
end;

Protect method that adds logs

procedure TQueueLog.AddLog(value: string);
begin
  FCriticalSection.Acquire;
  try
    FLog.Add(value);
  finally
    FCriticalSection.Release;
  end;
end;

And create a method that reads, in a protected way, the logs added

function TQueueLog.GetLogs: string;
begin
  FCriticalSection.Acquire;
  try
    result := FLog.CommaText;
    FLog.Clear;
  finally
    FCriticalSection.Release;
  end;
end;

And when you run, you logically reads the logs and operates with them in memory

procedure TQueueLog.Execute;
var
  count: integer;
  CurrentLogs: TStringList;
begin
  CurrentLogs := TStringList.Create;
  try
    while (not Self.Terminated) do
    begin
      CurrentLogs.CommaText := GetLogs;
      if (CurrentLogs.Count > 0) then
      begin
        FFile.LoadFromFile(FFileName);
        for count := 0 to pred(CurrentLogs.Count) do
        begin
          FFile.Add(CurrentLogs.Strings[count]);
        end;
        FFile.SaveToFile(FFileName);
        CurrentLogs.Clear;
        Sleep(200);
      end;
    end;
  finally
    CurrentLogs.Free;
  end;
end;
    
25.06.2014 / 16:16