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

多线程程序中两个线程在午夜准时挂起的问题排查求助

多线程程序中两个线程在午夜准时挂起的问题排查求助

我在程序里实现了3个辅助线程,功能分别是:

  • 第一个线程:每2分钟给外部设备发HTTP GET请求,检查在线状态,24小时运行都没问题
  • 第二个线程:做功耗“实时监控”,每2秒获取一次设备数据,通过主线程的进度条/标签展示
  • 第三个线程:整点从设备获取其他数据,写入文件,主线程会在用户请求时用这个文件生成图表

我觉得线程3不需要用Synchronize(),因为主线程是按需读取文件的。

本来所有线程都能稳定跑好几个小时,但一到午夜00:00,线程2和线程3就会挂起,线程1却能正常运行,主窗口也一直保持可用。重启程序后一切恢复正常,但到下一个午夜又会出问题。

我怀疑会不会是Execute()里的等待循环有问题?但目前还没找到线索。第一次来提问,要是信息不全或者说不清楚的地方还请见谅!


线程1的声明与实现

type
  TpingThread = class(TThread)
  private
    pingHTTP: TidHTTP;
  public
    constructor Create;
    procedure updateOnlineStatus;
  protected
    procedure Execute; override;
  end;

constructor TPingThread.Create;
begin
  Self.Suspended := False;
  Self.FreeOnTerminate := False;
  inherited Create(False);
end;

//"pings" all networkdevices, runs until program ends
procedure TPingThread.Execute;
Var delayTime: TDateTime;
    n: Byte;
    s: String;
begin
  pingHTTP := TIdHTTP.Create(NIL); pingHTTP.ConnectTimeout := 3000; pingHTTP.ReadTimeout := 3000;
  while NOT Terminated do begin
    for n := 0 to High(PingRec) do begin //8 devices
      blockRequests := True;
      try
        if (n = 0) then s := pingHTTP.Get('http://' + PingRec[n].Pingtarget)
        Else s := pingHTTP.Get('http://' + PingRec[n].Pingtarget + '/status');
        if (s = '') Then PingRec[n].PingResult := False
        Else PingRec[n].PingResult := True;
      except
        PingRec[n].PingResult := False;
      end;
    end; //For n
    Synchronize(updateonlineStatus);
    blockRequests := False;
    delayTime := system.DateUtils.IncSecond(Time,PingDelay);
    while (Time < DelayTime) do begin
      Sleep(100);
      Application.ProcessMessages;
      if (Terminated) then Break;
    end;
  end;
  pingHTTP.Free;
end;

procedure TpingThread.updateOnlineStatus;
Var aDev: TNetDevice; //component for a physical device
    n: Byte;
begin
  for n := 0 to High(PingRec) do begin
    aDev := fMain.FC(PingRec[n].PingDevice) AS TNetDevice;
    if (PingRec[n].PingResult = False) then aDev.Status := stDOffline
    Else begin
      case adev.Status of
        stDStandby,stDOffline: aDev.Status := stDOnline;
      end; //case
    end;
  end; //for n
end;

线程2和线程3的声明与实现

type
  TliveViewThread = class(TThread)
  private
    liveHTTP: TidHTTP;
  public
    PVPower,HTotal,L1,L2,L3: Extended;
    constructor Create;
    function currentPVPower(aIP: String): Double;
    function currentConsumption(aIP: String; Var L1,L2,L3: Extended): Extended;
    function getpowerFromStr(aStr: String): Extended;
    procedure updatePBars;
  protected
    procedure Execute; override;
  end;

type
  ThourThread = class(TThread)
  private
    hourHTTP: TidHTTP;
  public
    L1,L2,L3: Extended;
    constructor Create;
    function isTimeInRange: Boolean; //true if full hour
    function makeList(IP1,IP2,IP3: String): TStringList;
    procedure getValues(aString: String; Var PActive,PReturned: String);
    function getEntryandMakeList(fromList: TStringList; KeyName,delimiter: String): TStringList;
    procedure valuestoFile(V1,V2,V3: Extended);
  protected
    procedure Execute; override;
  end;

constructor TliveViewThread.Create;
begin
  Self.Suspended := False;
  Self.FreeOnTerminate := False;
  inherited Create(False);
end;

//updates some progressbars with values obtained from powermeasurement devices
procedure TliveViewThread.Execute;
Var delayTime: TDateTime; //WaitTimersimulation
begin
  liveHTTP := TIdHTTP.Create(NIL);
  while Not Terminated do begin
    //viewmode set when user activates a certain tab in main window
    if (ViewMode = vmLive) AND (blockEMs = False) then begin //LiveView
      //.Status checked and set by pingthread
      if (fMain.DEV6.Status = stDOnline) AND (fmain.DEV7.Status = stDOnline) then begin
        PVPower := currentPVPower(fMain.DEV6.DeviceIP);
        HTotal := currentConsumption(fMain.DEV7.DeviceIP,L1,L2,L3);
        Synchronize(updatePBars);
      end;
    end; //LiveView
    delayTime := system.DateUtils.IncSecond(Time,3);
    while (Time < DelayTime) do begin
      Sleep(100);
      Application.ProcessMessages;
      If (Terminated) then Break;
    end; //While delay
  end; //While NOT Terminated
  liveHTTP.Free;
end;

//fills values into a file that can be used by main thread at any time
constructor ThourThread.Create;
begin
  Self.Suspended := False;
  Self.FreeOnTerminate := False;
  inherited Create(False);
end;

procedure ThourThread.Execute;
Var delayTime: TDateTime; //WaitTimersimulation
begin
  hourHTTP := TIdHTTP.Create(NIL);
  while Not Terminated do begin
    if (fMain.DEV6.Status = stDOnline) AND (fmain.DEV7.Status = stDOnline) then begin
      if (isTimeInRange) then makeList(fMain.DEV7.DeviceIP,fMain.DEV6.DeviceIP,'');
    end;
    delayTime := system.DateUtils.IncSecond(Time,2);
    while (Time < DelayTime) do begin
      Sleep(10);
      Application.ProcessMessages;
      If (Terminated) then Break;
    end; //while delay
  end; //While NOT Terminated
  hourHTTP.Free;
end;

线程的启动与销毁

线程是在Form显示后启动的:

procedure TfMain.WmAfterShow(var Msg: TMessage);
begin
  ...
  if (AfterCreate) Then begin
    ....
    PingThread := TpingThread.Create;
    //blockRequests is used to make the program wait until all online-states are checked
    While (blockRequests) do begin
      Application.ProcessMessages;
      Sleep(50);
    end;
    ...
    liveViewThread := TliveViewThread.Create;
    hourThread := ThourThread.Create;
    ...
    afterCreate := False;
  end; //AfterCreate
end;

销毁线程的唯一位置:

procedure TfMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  ....
  if Assigned(PingThread) then PingThread.Terminate;
  if Assigned(liveViewThread) then liveViewThread.Terminate;
  if Assigned(hourThread) then hourThread.Terminate;
  PingThread.Free;
  liveViewThread.Free;
  hourThread.Free;
  ....
  canClose := True;
end;

已尝试的排查步骤

  • 最开始线程2和3是同一个线程,后来拆成两个,问题依旧
  • 调整过delayTime的时长,没用
  • 修改Sleep()的参数,没用
  • 注释掉设备在线状态的判断条件,问题还是存在
  • 到处加了try..except块,想避免“静默崩溃”,但没效果
  • 在线程循环的不同位置加了时间戳变量,发现线程挂起前的最后执行点都是在While Time < DelayTime循环之前

有没有大佬能帮我找找问题出在哪?


备注:内容来源于stack exchange,提问作者Maxxl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 15:54:37