工作线程池方法






3.63/5 (20投票s)
2003年2月6日
6分钟阅读

148762

4238
工作线程池方法
引言
在我们许多应用程序中,我们使用多线程来提高性能。我们大多数人已经知道并正在使用多线程应用程序。我们也知道线程创建是一项资源密集型工作。重用线程总是更好的,我们中的许多人也这样做。本文讨论了这样一种线程池(工作线程)架构。如果您能告知我发现的任何错误以及可以对此进行的任何改进,那将非常棒。
架构简述
线程池的主要架构如下所示的类图。
如您在上方的类图中所见,该应用程序主要有三个类,它们是:
-
CThPool
类:这是主线程池类。要使用线程池,您只需创建此类的实例。 -
CThread
类:此类封装了一个线程。您不必创建此类的任何实例。CThPool
类将返回一个CThread
类,您应该使用它。您也不应该销毁从CThPool
类实例收到的CThread
对象。 -
CThTarget
:这是所有目标对象的基类,可以将其作为目标传递给CThread
类。这是一个抽象类,其中run()
是纯虚方法。您应该在派生类中实现run()
。如类图所示,对于CYourClass
类。
使用代码
要使用线程池代码,我们必须执行以下操作:
- 创建池的实例。请参阅下面的示例代码。
CThpool *m_pThPool; m_pThPool = new CThPool();
- 如果想更改您的系统在任何给定时间可以创建的最大线程数,请更改最大池大小。默认值为 5。您也可以根据需要更改运行时池的大小以获得更好的性能。
m_pThPool->setMaxPoolSize(10); //You can pass any integer value.
- 创建一个类,如类图所示,该类将继承
CThTarget
类并实现纯虚函数void
run
()。请参阅下面的示例代码。class CThTargetSuper : public CThTarget { public: CThTargetSuper(); virtual ~CThTargetSuper(); void run(); } //------------------------------ void CThTargetSuper::run() { //Write your code here to do some process }
- 创建您上面定义的类的实例。
CThTargetSuper* pTarget = new CThTargetSuper();
- 当您需要使用线程来执行由类的
run()
函数定义的某些进程时。然后通过调用getThread()
函数从池中获取CThread
类的实例。然后调用您收到的CThread
实例的setTarget()
函数,并传递派生自CThTarget
的类的引用。CThread * pTh=NULL; pTh = m_pThPool->getThread(); //m_pThPool is an instance of CThPool if( pTh!=NULL) pTh->setTarget(pTarget);
注意:如您在上面的代码中看到的,从
CThPool::getThread()
函数收到的CThread
实例可能返回NULL
。这是因为所有线程都忙,没有空闲线程可用,并且池的当前大小已达到最大池大小。如果您希望作业得到处理,则可以在一段时间后重试。代码可以修改如下。但是,如果此代码由主 GUI 线程执行,则当线程长时间忙碌时,while 循环可能会导致问题。在这种情况下,此代码应在另一个线程中执行。CThread * pTh=NULL; pTh = m_pThPool->getThread(); //m_pThPool is an instance of CThPool while(pTh ==NULL) { Sleep(100); pTh = m_pThPool->getThread(); } if( pTh!=NULL) pTh->setTarget(pTarget);
- 调用
CThread::setTarget()
函数后,CThTarget
类型对象的run()
函数将在单独的线程中运行。当run()
函数完成时,线程将自动返回到池。注意:一旦线程完成,
CThTarget
对象就不能再次使用。这是因为一旦run()
函数完成,线程就会删除CThTarget
的实例。如果您想稍后使用CThTarget
对象,则必须通过将CThTarget
的自动删除标志设置为FALSE
来明确告诉线程,如下面的代码所示。CThTargetSuper* pTarget = new CThTargetSuper(); pTarget->setAutoDelete(FALSE);
默认值为
TURE
。您可以通过调用AutoDelete()
随时检查此值。如果标志设置为TRUE
,则返回TRUE
。切勿在从上一个
getThread()
对象收到的同一CThread
实例上再次调用setTarget()
。始终使用CThPool
实例上的getThread()
来获取CThread
对象,然后仅在該对象上调用setTarget()
一次。
附加功能
如何设置最大空闲池大小
有时我们的应用程序可能需要 100 个线程,但大多数时候所需的线程数少于 20 个。在这种情况下,没有必要在池中始终创建和保留 100 个线程。在这种情况下,我们可以将最大空闲池大小定义为 20。这将确保空闲池中不会有超过 20 个空闲线程。如果需要,将按需创建更多线程。下面提供了示例代码。
m_pThPool = new CThPool(); m_pThPool->setMaxPoolSize(100); //Maximum pool size m_pThPool->setMaxFreePoolSize(20); //Maximum free pool size
获取统计信息
有时我们需要统计信息,以了解在任何给定时间有多少线程正在忙,有多少线程空闲,池的大小是多少等等。我们可以通过调用池的一些方法来获取这些信息。在示例应用程序中,也使用了统计信息。用于获取统计信息的函数有:
int CThPool::getCurPoolSize() // returns the current pool size. int CThPool::getMaxPoolSize() // returns the maximum pool size. int CThPool::getMaxFreePoolSize() // returns the maximum free pool size. int CThPool::getFreePoolSize() // returns the number // of idle threads in the pool.
关于演示程序
演示程序使用一个线程池。最初,池以一个线程启动。最大池大小为 5。最大空闲线程大小为 2。在演示程序中,CThTargetSuper
是扩展 CThTarget
并实现 run()
方法的类。run 方法在休眠 100 毫秒后简单地递增进度条,直到它到达末尾。如果分配了更多的线程,进度条会移动得更快。当进度条到达进度条末尾时,run()
方法结束。然后 CThTargetSuper
的实例被线程删除(因为自动删除标志默认设置为 TRUE
),线程返回到空闲池,并保持挂起状态,直到分配了另一项工作。如果空闲池中的线程数量超过最大限制,线程也可能退出。
执行演示程序
启动程序后,首先单击 CreatePool 按钮来创建池。然后,您可以单击 AssignThread 按钮来分配一个新线程。您不能分配比最大池大小更多的线程。池中当前忙碌的线程数以及空闲线程和最大池大小显示在统计信息中。您可以通过单击“+”或“-”按钮来更改最大池大小。(上限定义为 20)。您还可以通过单击相应的“+”/“-”按钮来更改最大空闲池大小。您还可以按 Refresh 按钮来刷新统计信息。统计信息也会在大约每 2 秒自动更新。
要重复该过程,请单击 Reset 按钮,以便进度条将重置到初始位置。然后您可以再次分配线程以增加进度条的进度。