代码之家  ›  专栏  ›  技术社区  ›  Marek Jedliński

Delphi线程,它等待数据,处理数据,然后继续等待

  •  9
  • Marek Jedliński  · 技术社区  · 15 年前

    我需要在Delphi中创建一个具有以下特征的线程:

    • 等待主线程将数据添加到共享队列。
    • 处理队列中的所有数据,将结果返回到主线程(对于最后一部分,我只将消息发送到主窗口)。处理非常耗时,因此在工作线程处理以前的条目时,可以将新数据添加到队列中。
    • 继续等待,使用尽可能少的CPU周期。

    我不能向线程发送消息,因为它没有窗口句柄。

    我应该使用waitforobject的变体吗?如果是这样,等待会是什么?如果不是,那么如何让线程保持等待状态,然后在新数据进入队列时唤醒它?

    我读过 Multithreading - The Delphi Way 这似乎不能回答我的问题。也许 OmniThreadLibrary 我能做我需要做的事;我说不清,因为文件很少。一般来说,我对线程的了解还不够,无法弄清楚库是否能在这里提供帮助,以及如何使用它(甚至为什么要使用它而不是只使用tthread后代)。

    4 回复  |  直到 9 年前
        1
  •  13
  •   gabr    9 年前

    OmnithreadLibrary绝对可以帮助您。来自OTL发行版的测试5应该可以帮助您开始。

    在这个演示中,“开始”按钮创建线程并设置一些参数和计时器(如果不需要,可以在代码中删除)。change message“向线程发送消息,此消息在线程的omchangemessage方法中处理。然后,线程将一些信息发送回客户机(本演示中的omsendmessage,但您可以在将要在其中执行工作的同一条消息中执行此操作),并且主线程通过omniventmonitor组件接收此消息。”“停止”按钮停止工作线程。

    如果在线程忙时收到更多的消息,那么在您的辅助方法完成工作后,这些消息将被排队处理。当没有什么可做时,线程将使用进程中的零CPU周期等待下一条消息。

    编辑

    在Delphi 2009及更高版本中, Background Worker 模式提供了一个更简单的解决方案。

        2
  •  2
  •   user160694    15 年前

    waitForSingleObject()可以等待几种类型的同步对象。您可以使用Windows“事件”同步对象(与Delphi事件无关)。创建事件(在syncobjs、iirc中有一个delphi-tevent包装器),然后调用waitforsingleobject来等待该事件变为信号。当必须唤醒线程时,调用setEvent将事件置于信号状态,并返回waitForSingleObject。您可以使用waitformultipleobjects()让线程等待多个对象中的一个(或全部),它还将告诉您哪个对象已发出信号。

        3
  •  1
  •   mghie    15 年前

    即使没有窗口句柄,也可以向线程发送消息。只是使用 PostThreadMessage() 而不是 SendMessage() PostMessage() .如果您搜索 PostThreadMessage()。 在[Delphi]标签中-我不认为复制这里的所有内容是个好主意。

    但是,如果您不了解线程编程,那么从OTL开始而不是从低级的东西开始可能确实是一件好事。

        4
  •  1
  •   egon    15 年前

    下面是一个简单的例子,你如何做到…

    const
      WM_MY_RESULT = WM_USER + $1;
    
    type
      TMyThread = class(TThread)
      private
        FKilled: Boolean;
        FListLock: TRTLCriticalSection;
        FList: TList;
        FJobAdded: TEvent;
      protected
        procedure Execute; override;
        procedure DoJob(AJob: Integer);
      public
        constructor Create(CreateSuspended: Boolean);
        destructor Destroy; override;
        procedure Kill;
        procedure PushJob(AJob: Integer);
        function  JobCount: Integer;
        function  GetJob: Integer;
      end;
    
    
      TThreadingForm = class(TForm)
        lstResults: TListBox;
        se: TSpinEdit;
        btn: TButton;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure btnClick(Sender: TObject);
      private
        FThread: TMyThread;
        procedure OnMyResultMessage(var Msg: TMessage); message WM_MY_RESULT;
      public
        { Public declarations }
      end;
    
    var
      ThreadingForm: TThreadingForm;
    
    implementation
    
    {$R *.dfm}
    
    { TMyThread }
    
    constructor TMyThread.Create(CreateSuspended: Boolean);
    begin
      FKilled := False;
      InitializeCriticalSection(FListLock);
      FList := TList.Create;
      FJobAdded := TEvent.Create(nil, True, False, 'job.added');
      inherited;
    end;
    
    destructor TMyThread.Destroy;
    begin
      FList.Free;
      FJobAdded.Free;
      DeleteCriticalSection(FListLock);
      inherited;
    end;
    
    procedure TMyThread.DoJob(AJob: Integer);
    var
      res: Integer;
    begin
      res := AJob * AJob * AJob * AJob * AJob * AJob;
      Sleep(1000); // so it would take some time
      PostMessage(ThreadingForm.Handle, WM_MY_RESULT, res, 0);
    end;
    
    procedure TMyThread.Execute;
    begin
      inherited;
      while not FKilled or not Self.Terminated do
      begin
        EnterCriticalSection(FListLock);
        if JobCount > 0 then
        begin
          LeaveCriticalSection(FListLock);
          DoJob(GetJob)
        end
        else
        begin
          FJobAdded.ResetEvent;
          LeaveCriticalSection(FListLock);
          FJobAdded.WaitFor(10000);
        end;
      end;
    end;
    
    function TMyThread.GetJob: Integer;
    begin
      EnterCriticalSection(FListLock);
      try
        Result := Integer(FList[0]);
        FList.Delete(0);
      finally
        LeaveCriticalSection(FListLock);
      end;
    end;
    
    function TMyThread.JobCount: Integer;
    begin
      EnterCriticalSection(FListLock);
      Result := FList.Count;
      LeaveCriticalSection(FListLock);
    end;
    
    procedure TMyThread.Kill;
    begin
      FKilled := True;
      FJobAdded.SetEvent;
      Terminate;
    end;
    
    procedure TMyThread.PushJob(AJob: Integer);
    begin
      EnterCriticalSection(FListLock);
      try
        FList.Add(Pointer(AJob));
        FJobAdded.SetEvent;
      finally
        LeaveCriticalSection(FListLock);
      end;
    end;
    
    { TThreadingForm }
    
    procedure TThreadingForm.OnMyResultMessage(var Msg: TMessage);
    begin
      lstResults.Items.Add(IntToStr(Msg.WParam));
    end;
    
    procedure TThreadingForm.FormCreate(Sender: TObject);
    begin
      FThread := TMyThread.Create(False);
    end;
    
    procedure TThreadingForm.FormDestroy(Sender: TObject);
    begin
      FThread.Kill;
      FThread.WaitFor;
      FThread.Free;
    end;
    
    procedure TThreadingForm.btnClick(Sender: TObject);
    begin
      FThread.PushJob(se.Value);
    end;