如何在Delphi中编写线程数

时间:2009-09-29 10:58:01

标签: multithreading delphi file-io

我今天在Dobbs博士的网站上发现了这个 http://www.ddj.com/hpc-high-performance-computing/220300055?pgno=3 这是关于线程实现的一个很好的建议。 用Delphi中的TThread实现这一目标的最佳方法是什么? 谢谢 布赖恩

===来自Dobbs博士==============

使多线程可配置!程序中使用的线程数应始终可以从0(完全没有其他线程)配置为任意数字。这不仅可以实现最佳性能的定制,而且还可以证明它是一种很好的调试工具,有时候在客户端系统上出现未知竞争条件时也可以成为救星。我记得不止一种情况,客户可以通过关闭多线程来克服致命的错误。这当然不仅适用于多线程文件I / O.

考虑以下伪代码:

int CMyThreadManger::AddThread(CThreadObj theTask)
{
    if(mUsedThreadCount >= gConfiguration.MaxThreadCount())
        return theTask.Execute(); // execute task in main thread
    // add task to thread pool and start the thread
    ...
}

这种机制并不是很复杂(尽管可能需要的工作量比这里显示的要多一些),但它有时非常有效。它也可以与预构建的线程库一起使用,例如OpenMP或Intel的Threaded Building Blocks。考虑到此处显示的测量结果,最好包含多个可配置的线程计数(例如,一个用于文件I / O,一个用于核心CPU任务)。对于文件I / O和< 找到的核心数,默认值可能为0 >用于CPU任务。但是所有多线程都应该是可拆卸的。更复杂的方法甚至可能包括一些代码来测试多线程性能并设置自动使用的线程数,甚至可以单独用于不同的任务。

===================

4 个答案:

答案 0 :(得分:5)

我会创建一个抽象类TTask。此类用于执行任务。使用方法执行:

type

  TTask = abstract class
  protected
    procedure DoExecute; virtual; abstract;
  public
    procedure Execute;
  end;

  TTaskThread = class (TThread)
  private
    FTask : TTask;
  public
    constructor Create(const ATask: TTask); 
    // Assigns FTask and enables thread, free on terminate.

    procedure Execute; override; // Calls FTask.Execute.
 end;

方法Execute检查线程数。如果未达到max,则使用调用DoExecute的TTaskThread启动一个线程,并因此在线程中执行该任务。如果达到最大值,则直接调用DoExecute。

答案 1 :(得分:4)

就抽象任务类而言,

The answer by Gamecat是好的,但我认为调用DoExecute()来调用线程中的任务(因为文章本身也是如此)是一个坏主意。我总是将后台线程执行的任务排队,除非完全禁用线程,这就是原因。

考虑以下(人为的)情况,您需要执行三个独立的CPU绑定过程:

Procedure1_WhichTakes200ms;
Procedure2_WhichTakes400ms;
Procedure3_WhichTakes200ms;

为了更好地利用双核系统,您希望在两个线程中执行它们。您可以将后台线程的数量限制为一个,因此在主线程中,您拥有与内核一样多的线程。

现在第一个程序将在一个工作线程中执行,它将在200毫秒后完成。第二个过程将立即启动并在主线程中执行,因为单个已配置的工作线程已被占用,并且它将在400毫秒后完成。然后最后一个程序将在工作线程中执行,该工作线程现在已经休眠200毫秒,并将在200毫秒后完成。总执行时间为600毫秒,而在2/3的时间内,只有一个线程实际上正在进行有意义的工作。

您可以对程序(任务)进行重新排序,但在现实生活中,可能无法事先知道每项任务需要多长时间。

现在考虑使用线程池的常用方法。根据配置,您可以将池中的线程数限制为2(核心数),仅使用主线程将线程调度到池中,然后等待所有任务完成。使用上面排队的任务序列,线程1将执行第一个任务,线程2将执行第二个任务。在200毫秒之后,第一个任务将完成,第一个工作线程将从池中获取第三个任务,之后为空。在400毫秒之后,第二个和第三个任务都将完成,并且主线程将被解除阻塞。执行总时间为400毫秒,在这段时间内两个核心的负载均为100%。

至少对于CPU绑定的线程,始终为OS调度程序排队的工作至关重要。在主线程中调用DoExecute()会干扰它,不应该这样做。

答案 2 :(得分:0)

我通常只有一个继承自TThread的类,一个从队列或堆栈中获取“工作项”,并在没有更多项可用时暂停它们。然后,主程序可以决定实例化和启动此线程的实例数。 (使用此配置值)。

这个'工作项队列'也应足够智能,以便在需要时(当限制允许时),当工作项排队或线程处理完工作项时,恢复挂起的线程或创建新线程。

答案 3 :(得分:0)

如果您希望查看(http://www.csinnovations.com/framework_overview.htm),我的框架允许配置文件中任何线程的线程池计数。