You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程应用实时保存TXT日志且避免应用冻结的最优方法

多线程Delphi日志无冻结实现方案

你的代码出现UI冻结的核心原因是:用Synchronize把文件IO操作强行放到主线程执行,而文件读写是阻塞性操作,主线程被卡住后无法响应用户操作,导致冻结。

下面是解决思路和具体实现方案:

一、核心优化方向

  • 永远不要在主线程做文件IO,把日志写入逻辑放到独立的工作线程
  • 用线程安全的队列传递日志消息,避免多线程直接竞争文件资源
  • 减少文件打开/关闭的频次,降低IO开销

二、具体实现代码(独立日志线程+队列)

这个方案用专门的日志线程处理所有写入操作,其他线程只需要把日志消息扔进队列,完全不阻塞主线程:

unit LogUnit;

interface

uses
  System.Classes, System.SysUtils, SyncObjs;

type
  TLogMsg = record
    Msg: string;
    ShowUI: Boolean;
  end;

  TLogThread = class(TThread)
  private
    FQueue: array of TLogMsg;
    FCriticalSection: TCriticalSection;
    FLogFile: TextFile;
    FCurrentDate: TDate;
    FEvent: TEvent;
    procedure OpenLogFile(const Date: TDate);
    procedure CloseLogFile;
    procedure UpdateMemo(const Msg: string);
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AddMsg(const AMsg: string; AShowUI: Boolean);
  end;

var
  LogThread: TLogThread;

implementation

uses
  MainForm; // 替换成你的主窗体单元名称

{ TLogThread }

constructor TLogThread.Create;
begin
  inherited Create(True);
  FCriticalSection := TCriticalSection.Create;
  FEvent := TEvent.Create(nil, False, False, '');
  FCurrentDate := 0;
  FreeOnTerminate := False;
  Resume;
end;

destructor TLogThread.Destroy;
begin
  CloseLogFile;
  FEvent.SetEvent; // 唤醒线程让它退出
  WaitFor;
  FEvent.Free;
  FCriticalSection.Free;
  inherited;
end;

procedure TLogThread.OpenLogFile(const Date: TDate);
var
  FileName: string;
begin
  CloseLogFile;
  FileName := ExtractFilePath(ParamStr(0)) + 'Logs\FILE_' + FormatDateTime('dd_mm_yyyy', Date) + '.txt';
  ForceDirectories(ExtractFilePath(FileName)); // 确保日志目录存在,避免创建文件失败
  AssignFile(FLogFile, FileName);
  if FileExists(FileName) then
    Append(FLogFile)
  else
    Rewrite(FLogFile);
  FCurrentDate := Date;
end;

procedure TLogThread.CloseLogFile;
begin
  if FCurrentDate <> 0 then
  begin
    CloseFile(FLogFile);
    FCurrentDate := 0;
  end;
end;

procedure TLogThread.UpdateMemo(const Msg: string);
begin
  MainForm.Form1.Memo2.Lines.Add(Msg);
end;

procedure TLogThread.AddMsg(const AMsg: string; AShowUI: Boolean);
var
  LogMsg: TLogMsg;
begin
  FCriticalSection.Enter;
  try
    LogMsg.Msg := AMsg;
    LogMsg.ShowUI := AShowUI;
    SetLength(FQueue, Length(FQueue)+1);
    FQueue[High(FQueue)] := LogMsg;
  finally
    FCriticalSection.Leave;
  end;
  FEvent.SetEvent; // 唤醒日志线程处理新消息
end;

procedure TLogThread.Execute;
var
  I: Integer;
  CurrentMsg: TLogMsg;
  Today: TDate;
begin
  while not Terminated do
  begin
    // 等待新消息,没有消息时线程休眠,不占用CPU
    if FEvent.WaitFor(1000) = wrTimeout then
      Continue;

    FCriticalSection.Enter;
    try
      if Length(FQueue) = 0 then
      begin
        FEvent.ResetEvent;
        FCriticalSection.Leave;
        Continue;
      end;
      // 取出队列第一个消息
      CurrentMsg := FQueue[0];
      // 移除第一个消息
      for I := 0 to High(FQueue)-1 do
        FQueue[I] := FQueue[I+1];
      SetLength(FQueue, Length(FQueue)-1);
      if Length(FQueue) = 0 then
        FEvent.ResetEvent;
    finally
      FCriticalSection.Leave;
    end;

    // 处理日志写入
    Today := Date;
    if Today <> FCurrentDate then
      OpenLogFile(Today);

    try
      Writeln(FLogFile, CurrentMsg.Msg); // 加换行让日志更易读
      Flush(FLogFile); // 强制写入磁盘,避免缓存丢失
    except
      // 这里可以加异常日志,比如写入失败时记录到临时文件
    end;

    // 处理UI更新(只在需要时用Synchronize,且只做UI操作)
    if CurrentMsg.ShowUI and not MainForm.Form1.CheckBox1.Checked then
    begin
      Synchronize(procedure
                  begin
                    UpdateMemo(CurrentMsg.Msg);
                  end);
    end;
  end;
  CloseLogFile;
end;

initialization
  LogThread := TLogThread.Create;

finalization
  LogThread.Terminate;
  LogThread.Free;

end.

三、使用方法

在任何线程(包括主线程)里直接调用:

LogThread.AddMsg('用户登录成功', True);

第一个参数是日志内容,第二个参数控制是否显示到Memo里。

四、其他优化建议

  • 可以给日志自动加上时间戳,比如在AddMsg里拼接FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + ': ' + AMsg
  • 如果日志量极大,可以考虑批量写入队列,减少线程唤醒次数
  • 异常处理部分可以补充逻辑,比如文件被占用时尝试重试,或者切换到临时日志文件

内容的提问来源于stack exchange,提问作者Luiz Felipe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:54:58