在多线程应用程序上创建后某些线程不执行

Some threads does not execute after creating on Multi-threaded app

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

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 线程在 send/recv 个线程期间挂起。

任何帮助将不胜感激。

不要从您的话题中调用 Application.ProcessMessages。这是一件可怕的事情。你所能期望的最好的结果就是壮观的失败。它在 错误的线程 .

上调用了主线程上应该是 运行 的代码

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

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

因此,当您使大多数工作线程的成员成为成员时,警钟就在尖叫 public:

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;

您需要重新评估工作线程的职责,并适当地封装该工作。 (Only call Synchronize() for code that should be synchronised!) 但是,我不熟悉你使用的组件,你可能会发现它们写得不好,因此不适合多线程。

其他问题

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

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

一些猜测

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

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