多线程程序中两个线程在午夜准时挂起的问题排查求助
多线程程序中两个线程在午夜准时挂起的问题排查求助
我在程序里实现了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
相关产品推荐
相关产品推荐

