How to implement a process queue in Delphi 6?

Asked

Viewed 747 times

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 recording logs with Delphi 6. In the newer versions I have the possibility 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 don’t 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;

So, how to prevent access violation when trying to add an item to FLog for AddLog when the thread is running? There is another, correct, way to implement this?

1 answer

4


To prevent errors in this type of operation you must control the competition using for example a Critical Section.

For this you will need an object of the 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 the 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 added logs

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

And when running you read, in a protected way, 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;

Browser other questions tagged

You are not signed in. Login or sign up in order to post.