Delphi线程等待数据,处理它,然后恢复等待

发布于 2024-08-11 08:04:04 字数 643 浏览 6 评论 0原文

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

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

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

我应该使用 WaitForObject 的某种变体吗?如果是这样,还等什么?如果没有,那么我如何保持线程等待,然后在新数据进入队列时唤醒它?

我已阅读多线程 - Delphi 方式,这似乎没有回答我的问题。也许 OmniThreadLibrary 可以满足我的需要;我无法判断,因为文档很少。一般来说,我对线程了解不够,无法弄清楚该库是否会在这里提供帮助以及如何使用它(甚至为什么要使用它而不是仅仅使用 TThread 后代)。

I need to create a thread in Delphi with the following characteristics:

  • Waits until the main thread adds data to a shared queue.
  • Processes all the data in the queue, returning the results to main thread (for this last part I'll just send messages to the main window). Processing is time-consuming, so new data may be added to the queue while the worker thread is processing previous entries.
  • Resumes waiting, using as little cpu cycles as possible.

I cannot send messages to the thread, since it does not have a window handle.

Should I be using some variant of WaitForObject? If so, what would the wait be for? If not, then how can I keep the thread waiting, then awake it when new data comes down the queue?

I've read Multithreading - The Delphi Way, which doesn't seem to answer my question. Perhaps OmniThreadLibrary can do what I need; I can't tell since there's little documentation. I don't know enough about threads in general to figure out if the library will help here and how to use it (or even why to use it instead of just working with TThread descendants).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

羁〃客ぐ 2024-08-18 08:04:05

OmniThreadLibrary 绝对可以在这方面为您提供帮助。 OTL 发行版中的测试 5 应该可以帮助您入门。

在此演示中,“开始”按钮创建线程并设置一些参数和计时器(如果不需要,您可以在代码中删除它们)。 “更改消息”向线程发送一条消息,该消息在线程的 OMChangeMes​​sage 方法中处理。然后,线程将一些信息发送回客户端(本演示中为 OMSendMessage,但您可以在您将在其中执行工作的同一消息中执行此操作),并且主线程通过 OmniEventMonitor 组件接收此消息。 “停止”按钮停止工作线程。

如果在您的线程繁忙时有更多消息到达,它们将在您的工作方法完成其工作后立即排队并进行处理。当无事可做时,线程将使用进程中的零 CPU 周期等待下一条消息。

编辑

在Delphi 2009及更高版本中,后台工作人员模式提供了更简单的解决方案。

OmniThreadLibrary can definitely help you here. Test 5 from the OTL distribution should help you started.

In this demo, "Start" button creates the thread and sets some parameters and timer (which you can remove in your code if not needed). "Change message" sends a message to the thread and this message is processed in thread's OMChangeMessage method. Thread then sends some information back to the client (OMSendMessage in this demo, but you can do this in the same message you'll be doing your work in) and main thread receives this message via the OmniEventMonitor component. "Stop" button stops the worker thread.

If more messages arrive while your thread is busy, they will be queued and processed as soon as your worker method has completed its work. When there's nothing to do, thread will wait for the next message using zero CPU cycles in the process.

EDIT

In Delphi 2009 and above, the Background Worker pattern provides a simpler solution.

伴随着你 2024-08-18 08:04:05

WaitForSingleObject() 可以等待多种类型的同步对象。您可以使用 Windows“事件”同步对象(与 Delphi 事件无关)。您创建事件(SyncObjs、IIRC 中有一个 Delphi TEvent 包装器),然后调用 WaitForSingleObject 等待该事件发出信号。当您必须唤醒线程时,您可以调用 SetEvent 将事件置于有信号状态,然后 WaitForSingleObject 返回。您可以使用 WaitForMultipleObjects() 让线程等待多个对象中的一个(或全部) - 它还会告诉您哪个对象已发出信号。

WaitForSingleObject() can wait on several types of synchronization objects. You can use a Windows "event" synchronization object (which has nothing to do with a Delphi event). You create the event (there's a Delphi TEvent wrapper in SyncObjs, IIRC), and call WaitForSingleObject to wait for that event to become signalled. When you have to awake the thread, you call SetEvent to put the event in the signalled state and WaitForSingleObject returns. You can have a thread wait for one (or all) of multiple object using WaitForMultipleObjects() - it will also tell you which object became signalled.

秋心╮凉 2024-08-18 08:04:05

您绝对可以向线程发送消息,即使它没有窗口句柄。只需使用 PostThreadMessage() 而不是 SendMessage()PostMessage()。如果您在 [delphi] 标记中搜索 PostThreadMessage(),则会在 StackOverflow 上找到更多信息 - 我认为在这里复制所有内容并不是一个好主意。

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

You can definitely send messages to a thread, even though it doesn't have a window handle. Just use PostThreadMessage() instead of SendMessage() or PostMessage(). There will be more information here on StackOverflow if you search for PostThreadMessage() in the [delphi] tag - I don't think it's a good idea to duplicate everything here.

But if you are not knowledgeable about thread programming, then starting with OTL instead of the low level stuff may indeed be a good thing.

胡大本事 2024-08-18 08:04:05

这是一个简单的例子,告诉你如何做到这一点......

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;

Here's a simple example how you can do it...

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文