【问题标题】:Some threads does not execute after creating on Multi-threaded app在多线程应用程序上创建后某些线程不执行
【发布时间】:2016-09-10 13:45:34
【问题描述】:

我目前正在开发一个定期执行一些 AT 命令的程序。 我试图让它多线程,因为这个程序应该同时与 8 个 GSM 调制解调器一起工作。 这是我作为 TWorkerThread 扩展的 TThread 类:

TWorkerThread = class(TThread)
private
  FThreadJob       : TThreadJobs;
  FSimNum          : Word;
  FZylGSM          : TZylGSM;
  SL_AT            : TStringList;
  FSignalGauge     : TsGauge;
  procedure SyncProc;

public
  TerminateThread   : Boolean;
  constructor Create;
  property ThreadJob  : TThreadJobs read FThreadJob write FThreadJob;
  property ZylGSM     : TZylGSM read FZylGSM write FZylGSM;
  property SimNum     : Word read FSimNum write FSimNum;
  property SignalGauge: TsGauge read FSignalGauge write FSignalGauge;
protected
  procedure Execute; override;
end;

我的线程方法的主体:

    constructor TWorkerThread.Create;
    begin
      inherited Create(True);
      if Not Assigned(SL_AT) then SL_AT := TStringList.Create;
      SL_AT.Clear;
      FThreadJob := tjNone;
      TerminateThread := False;
      FreeOnTerminate := True;
    end;



    procedure TWorkerThread.Execute;
    begin
      inherited;
      if FThreadJob = tjNone then Exit;
      while TerminateThread=False do Synchronize(SyncProc);
    end;




    procedure TWorkerThread.SyncProc;
    var
      ts : String;
      SignalStrength : Byte;
    begin
      if bTerminateFlag then TerminateThread := True;
      if TerminateThread then Exit;
      case FThreadJob of
        tjOperatorName  : ;

        tjSignalQuality :
          begin
            FZylGSM.ExecuteATCommand('AT+CSQ', SL_AT);
            if (SL_AT.Count>2) And (Pos('OK', SL_AT[2])>0) then begin
              ts := Copy(SL_AT[1], Pos(':', SL_AT[1])+1, Length(SL_AT[1]));
              ts := Trim(ts);
              if ts = '99' then ts:='0';
              SignalStrength := StrToIntDef(ts, 0);
              SignalGauge.Progress := SignalStrength;
            end;
            if bTerminateFlag then TerminateThread := True;
    //        Application.ProcessMessages;
          end;
      end;
    end;

我使用 for 循环创建了 8 个这样的线程:

DevPorts.GSM_Ports[i].WorkerThread := TWorkerThread.Create;
DevPorts.GSM_Ports[i].WorkerThread.ThreadJob := tjSignalQuality;
DevPorts.GSM_Ports[i].WorkerThread.SimNum := i+1;
DevPorts.GSM_Ports[i].WorkerThread.SignalGauge := FindComponent('Sig_'+IntToStr(i)) as TsGauge;
DevPorts.GSM_Ports[i].WorkerThread.ZylGSM := DevPorts.GSM_Ports[i].Comm;
DevPorts.GSM_Ports[i].WorkerThread.Start;

当我评论“Application.ProcessMessages”时程序按预期工作,问题是当我在“TWorkerThread.SyncProc”中使用“Application.ProcessMessages”时,我的一些线程没有执行。我知道在线程函数中使用 ProcessMessage 可能是错误的,但我这样做是因为主 GUI 线程在线程的发送/接收期间挂起。

任何帮助将不胜感激。

【问题讨论】:

  • 您的参赛作品设计是假的。所有工作都在主线程中执行。删除所有线程代码并在主线程中完成工作。或者使用线程并在线程中完成工作。是的,ProcessMessages 是个坏主意。永远不要使用它。
  • @DavidHeffernan 感谢您的评论。正如我所提到的,我必须同时对每个 GSM 调制解调器进行发送/接收。所以,我不能在主线程中完成所有工作。您能否告诉我为什么所有工作都在我的代码的主线程中执行?
  • 因为您使用了 Synchronize。你的代码有很多问题。 Stack Overflow 并不是真正的调试个人代码。它是关于对其他人有用的一般问题和答案。

标签: multithreading delphi delphi-10-seattle


【解决方案1】:

不要从你的线程中调用Application.ProcessMessages。这是一件可怕的事情。你所能期望的最好的结果就是惊人的失败。它调用应该在错误线程上的主线程上运行的代码。

您的主 GUI 线程挂起的原因是因为您没有运行任何多线程。 while TerminateThread=False do Synchronize(SyncProc); 行正在同步所有内容以在主线程上运行。所以目前你的线程毫无意义。

Synchronize() 的目的是允许线程协调对共享数据的访问,这样您就不必处理竞争条件。但是,理想是尽可能共享少量数据,以便您的线程可以彼此独立工作(以及主线程),不必担心在其控制下的值会在不适当的时间被更改。

因此,当您公开大多数工作线程的成员时,警钟就会响起:

public
  TerminateThread   : Boolean;
  constructor Create;
  property ThreadJob  : TThreadJobs read FThreadJob write FThreadJob;
  property ZylGSM     : TZylGSM read FZylGSM write FZylGSM;
  property SimNum     : Word read FSimNum write FSimNum;
  property SignalGauge: TsGauge read FSignalGauge write FSignalGauge;

您需要重新评估工作线程的职责,并适当地封装这些工作。 (仅调用 Synchronize() 获取应同步的代码!)但是,我不熟悉您正在使用的组件,您可能会发现它们写得不好,因此不适合多线程。

其他问题

除了你有明显的直接问题。还有一些错误表明您对多线程开发的理解存在差距。

  • 不要从TWorkerThread.Execute 呼叫inherited。祖先方法是抽象的 - 没有实现,无法调用。尽管 Delphi 编译器慷慨地保护您免于犯错,但它仍然是一个错误。
  • 您对TerminateThread : Boolean; 的实现复制了TThread 中内置的现有功能。使用 Delphi 已经提供的东西,而不是重新发明轮子。
  • 我看不到您声明或设置bTerminateFlag 的位置。我的直觉是它是全球性的。使用多线程全局变量就像站在一个装满敞开的火药桶的房间里玩弄燃烧的火把一样。

一些猜测

我可以根据您在显示的代码中尝试执行的操作进行一些猜测。

  • 您似乎正在更新TsGuage 实例以直观地指示每个设备的信号强度。这是一个必须同步的 GUI 更新。
  • FZylGSM.ExecuteATCommand('AT+CSQ', SL_AT); 行似乎是您与设备交互的地方。它可能也是最慢的,也是您想要从主线程处理的内容。如果可能,这不应该同步。 但是,如前所述,这样做的可行性取决于该组件的实现。
  • 也就是说,您应该同步的唯一行似乎是:SignalGauge.Progress := SignalStrength;

【讨论】:

  • ProcessMessages 在此处从主线程调用。接下来发生的是一个长期运行的评论跟踪,其中询问者根据您的建议更改代码并询问为什么它不起作用。祝你好运。
  • @Craig 非常感谢您的评论和您的时间。你完全正确。 FZylGSM.ExecuteAtCommand 是最慢的,这就是我尝试使程序多线程的原因。我牢记您的建议,非常感谢您花时间分析我糟糕的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 1970-01-01
  • 2020-04-06
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多