如何在Delphi中编程设置线程数量

6
我今天在Dr Dobbs网站上发现了这篇文章: http://www.ddj.com/hpc-high-performance-computing/220300055?pgno=3 它提供了关于线程实现的好建议。 我想知道在Delphi中使用TThread实现这个最好的方法是什么? 谢谢 Brian
=== 来自 Dr 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任务)。默认值可能为0(文件I/O)和<数字核心数量>(CPU任务)。但所有多线程都应该是可分离的。更复杂的方法甚至可能包括一些代码来测试多线程性能,并自动设置使用的线程数,甚至可能为不同任务单独设置线程数。
5个回答

5
我会创建一个抽象类TTask。该类旨在执行任务。使用Execute方法:
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检查线程数。如果未达到最大值,则使用TTaskThread启动一个线程,调用DoExecute并在线程中执行任务。如果已经达到最大值,则直接调用DoExecute。


4
Gamecat的答案 对于抽象任务类而言是不错的,但我认为在调用DoExecute()时在调用线程中执行任务(正如本文所做)是一个坏主意。我总是将任务排队以由后台线程执行,除非完全禁用线程,原因如下。
考虑以下(人为制造的)情况,在这种情况下,您需要执行三个独立的CPU绑定过程:
Procedure1_WhichTakes200ms;
Procedure2_WhichTakes400ms;
Procedure3_WhichTakes200ms;

为了更好地利用您的双核系统,您希望将它们分别在两个线程中执行。您将将后台线程数量限制为1个,因此与主线程一起,您拥有与核心数相同的线程。
首先,第一个过程将在工作线程中执行,并在200毫秒后完成。第二个过程将立即启动并在主线程中执行,因为单个配置的工作线程已经被占用,它将在400毫秒后完成。然后,最后一个过程将在工作线程中执行,该线程已经睡眠了200毫秒,现在将在200毫秒后完成。总执行时间为600毫秒,其中2/3的时间只有其中一个线程实际上正在进行有意义的工作。
您可以重新排列过程(任务),但在现实生活中,预先知道每个任务需要多长时间可能是不可能的。
现在考虑使用线程池的常见方法。根据配置,您将限制池中的线程数为2(核心数),仅使用主线程将线程调度到池中,然后等待所有任务完成。对于上面队列任务的序列,线程1将执行第一个任务,线程2将执行第二个任务。 200毫秒后,第一个任务将完成,第一个工作线程将从池中获取第三个任务,此后池为空。在400毫秒后,第二个和第三个任务都将完成,主线程将被解除阻塞。执行总时间为400毫秒,在此期间两个核心的负载达到了100%。
至少对于CPU限制的线程,始终让OS调度器排队工作非常重要。在主线程中调用DoExecute()会干扰它,因此不应这样做。

0

0

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

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


5
完全没有必要挂起和恢复线程。让每个线程等待事件或信号量,或者使用WaitMessage()与线程消息循环(对于 OLE 可能是必要的)。这个主题在 [delphi] 下有很多讨论,请阅读,但最好忽略那些认为 Embarcadero 和 Microsoft 开发人员不如他们的争论,并且试图告诉你使用 Suspend()Resume() 是可以的。 - mghie

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接