【问题标题】:Which wait method to call in an infinite waiting thread for Delphi XE2在 Delphi XE2 的无限等待线程中调用哪个等待方法
【发布时间】:2012-10-05 17:15:07
【问题描述】:

如果我有一个

While not terminated do
begin
     doStuff;

end

在 Delphi XE2 线程的执行方法中循环,我不想让它成为我所有的失败者。

我应该怎么称呼,

在 Delphi 7 中,这很简单,我会调用 Sleep(X),其中 X 与我认为线程的有趣程度成反比。

但现在,我有

SpinWait(X);

调用 YieldProcessor X 次

Yield;

调用windows函数“SwitchToThread”。

我应该使用其中任何一个还是应该只设置线程的优先级?

【问题讨论】:

  • 等待事件有什么问题?在这种情况下,线程可以进入休眠状态,直到事件发出信号。
  • 您必须使用适当的等待同步对象。 Sleep 永远不是答案。绝不。永远。
  • @DavidHeffernan 更真实的话很少说出来。

标签: multithreading delphi delphi-xe2


【解决方案1】:

SpinWait 在不放弃处理器的情况下浪费时间。就像Sleep,但在延迟期间没有让出控制权给任何其他线程。如果您没有多个内核,那么这完全是一种浪费,因为在您旋转时没有其他线程可以做任何事情。据我所知,Yield 类似于Sleep(0),只是如果没有其他线程准备好运行,则调用线程会立即继续。

如果您知道您的线程确实无事可做,那么这些听起来都不像您想要的。

最好的解决方案是找到或建立一些可等待的对象(如信号量、事件或进程句柄),您可以等待它们发出信号。这样您就不必再为醒来而烦恼了,这样您就可以轮询您的状态并再次入睡。

【讨论】:

  • 感谢您的指点。我正在做一些又快又脏的事情。这里运行的进程不是长期存在的后台线程。我正在尝试修复这个“threadpool.pas”单元,它更像是一个“deadpool.pas”,买了一个最近离开我们公司的家伙,他混合了产量、Windows 事件句柄和 tthreadedqueues。一旦我开始运行,我会把合法的 delphi 事件放回去。
  • 操作系统已经提供了线程池。备受赞誉的 OmniThreadLibrary 也是如此。与其花时间弄清楚如何修复损坏的东西,不如把它花在改用已知可以工作的东西上。
  • @Peter:如果你想替换坏线程池,有什么办法可以重做接口作为OTL 的包装器?
  • @Mason 我会尝试下一个版本。但这是“上周需要做的事情”之一。至少该单元是通用的并且足够短,可以完全报废和更换。
  • Delphi 线程池微不足道。 TObjectQueue、Tsemaphore、TcriticalSection 和一些 TThreads。大概是 30-40 行。
【解决方案2】:

线程池示例:

unit ThreadPool;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, contnrs, syncobjs;


    type

    TpooledTask=class(TObject)
    private
      FonComplete:TNotifyEvent;
    protected
      Fparam:TObject;
      procedure execute; virtual; abstract;
    public
      constructor create(onComplete:TNotifyEvent;param:TObject);
    end;

    TThreadPool=class(TObjectQueue)
    private
      access:TcriticalSection;
      taskCounter:THandle;
      threadCount:integer;
    public
      constructor create(initThreads:integer);
      procedure addTask(aTask:TpooledTask);
    end;

    TpoolThread=class(Tthread)
    private
      FmyPool:TThreadPool;
    protected
      procedure Execute; override;
    public
      constructor create(pool:TThreadPool);
    end;

    implementation

    { TpooledTask }

    constructor TpooledTask.create(onComplete: TNotifyEvent; param: TObject);
    begin
      FonComplete:=onComplete;
      Fparam:=param;
    end;

    { TThreadPool }

    procedure TThreadPool.addTask(aTask: TpooledTask);
    begin
      access.acquire;
      try
        push(aTask);
      finally
        access.release;
      end;
      releaseSemaphore(taskCounter,1,nil); // release one unit to semaphore
    end;

    constructor TThreadPool.create(initThreads: integer);
    begin
      inherited create;
      access:=TcriticalSection.create;
      taskCounter:=createSemaphore(nil,0,maxInt,'');
      while(threadCount<initThreads) do
      begin
        TpoolThread.create(self);
        inc(threadCount);
      end;
    end;

    { TpoolThread }

    constructor TpoolThread.create(pool: TThreadPool);
    begin
      inherited create(true);
      FmyPool:=pool;
      FreeOnTerminate:=true;
      resume;
    end;

    procedure TpoolThread.execute;
    var thisTask:TpooledTask;
    begin
      while (WAIT_OBJECT_0=waitForSingleObject(FmyPool.taskCounter,INFINITE)) do
      begin
        FmyPool.access.acquire;
        try
          thisTask:=TpooledTask(FmyPool.pop);
        finally
          FmyPool.access.release;
        end;
        thisTask.execute;
        if assigned(thisTask.FonComplete) then thisTask.FonComplete(thisTask);
      end;
    end;

    end.

【讨论】:

  • 天哪,这代码看起来一模一样,他肯定是从网上弄出来的!
  • 好吧,可能不会,我要试一试,看看结果如何。他有一些 TThreadManager 介于两者之间。只是有几件事是相同的。
  • 自 D3 以来一直使用基本相同的设计。好的,这个精简版没有内置异常陷阱等,但我从来没有失败过。
  • 'TThreadManager' - 微管理线程是错误的......'非常困难'并且容易出现'与所需功能的异常和意外偏差'。
  • 哇,同样的结果,一定是他编程的另一部分内存泄漏!但是当我试图杀死它时,你的线程池实际上已经死了,非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-23
  • 2021-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多