POCO C++库学习和分析 -- 线程 (二)

POCO C++库学习和分析 --  线程 (二)


3.  线程池

3.1线程池的基本概念

       首先我们来明确线程池的一些概念。

       什么是线程池?线程池的好处?

       池的英文名:POOL,可以被理解成一个容器。线程池就是放置线程对象的容器。我们知道线程的频繁创建、销毁,是需要耗费一点的系统资源的,如果能够预先创建一系列空线程,在需要使用线程时侯,从线程池里,直接获取IDLE线程,则省去了线程创建的过程,当有频繁的线程出现的时候对性能有比较大的好处,程序执行起来将非常效率。

       什么时候推荐使用线程池?

       很明显,线程越频繁的被创建和释放,越是能体现出线程池的作用。这时候当然推荐使用线程池。

       什么时候不推荐使用线程池?

       推荐线程池使用的反面情况喽。

       比如长时间运行的线程(线程运行的时间越长,其创建和销毁的开销在其生命周期中比重越低)。

       需要永久标识来标识和控制线程,比如想使用专用线程来终止该线程,将其挂起或按名称发现它。因为线程池中的线程都是平等的。

       线程池需要具备的元素

  •        线程池要有列表,可以用来管理多个线程对象。
  •        线程池中的线程,具体执行的内容,可自定义。
  •        线程池中的线程,使用完毕后,还能被收回,供下次使用。
  •        线程池要提供获取空闲(IDLE)线程方法。当然这个方法可以被封装在线程池中,成为其内部接口。

 

3.2 Poco中线程池实现

       先看一看Poco中内存池的类图吧。


       对于Poco中的线程池来说,设计上分成了两层。第一层为ThreadPool,第二层为PooledThread对象。

       第一层中,ThreadPool负责管理线程池,定义如下:

class ThreadPool
{
public:
	ThreadPool(int minCapacity = 2,
		int maxCapacity = 16,
		int idleTime = 60,
		int stackSize = POCO_THREAD_STACK_SIZE);
	ThreadPool(const std::string& name,
		int minCapacity = 2,
		int maxCapacity = 16,
		int idleTime = 60,
		int stackSize = POCO_THREAD_STACK_SIZE);
	~ThreadPool();
	void addCapacity(int n);
	int capacity() const;
	void setStackSize(int stackSize);
	int getStackSize() const;
	int used() const;
	int allocated() const;
	int available() const;
	void start(Runnable& target);
	void start(Runnable& target, const std::string& name);
	void startWithPriority(Thread::Priority priority, Runnable& target);
	void startWithPriority(Thread::Priority priority, Runnable& target, const std::string& name);
	void stopAll();
	void joinAll();
	void collect();
	const std::string& name() const;
	static ThreadPool& defaultPool();

protected:
	PooledThread* getThread();
	PooledThread* createThread();

	void housekeep();

private:
	ThreadPool(const ThreadPool& pool);
	ThreadPool& operator = (const ThreadPool& pool);

	typedef std::vector<PooledThread*> ThreadVec;

	std::string _name;
	int _minCapacity;
	int _maxCapacity;
	int _idleTime;
	int _serial;
	int _age;
	int _stackSize;
	ThreadVec _threads;
	mutable FastMutex _mutex;
};

       从ThreadPool的定义看,它是一个PooledThread对象的容器。职责分成两部分:

       第一,维护和管理池属性,如增加线程池线程数目,返回空闲线程数目,结束所有线程

       第二,把需要运行的业务委托给PooledThread对象,通过接口start(Runnable& target)

void ThreadPool::start(Runnable& target)
{
	getThread()->start(Thread::PRIO_NORMAL, target);
}
       函数getThread()为ThreadPool的私有函数,作用是获取一个空闲的PooledThread线程对象,实现如下

PooledThread* ThreadPool::getThread()
{
	FastMutex::ScopedLock lock(_mutex);

	if (++_age == 32)
		housekeep();

	PooledThread* pThread = 0;
	for (ThreadVec::iterator it = _threads.begin(); !pThread && it != _threads.end(); ++it)
	{
		if ((*it)->idle()) pThread = *it;
	}
	if (!pThread)
	{
		if (_threads.size() < _maxCapacity)
		{
            pThread = createThread();
            try
            {
                pThread->start();
                _threads.push_back(pThread);
            }
            catch (...)
            {
                delete pThread;
                throw;
            }
		}
		else throw NoThreadAvailableException();
	}
	pThread->activate();
	return pThread;
}

         第二层中PooledThread对象为一个在线程池中线程。作为线程池中的线程,其创建于线程池的创建时,销毁于线程池的销毁,生命周期同线程池。在其存活的周期中,状态可分为running task和idle。running状态为正在运行业务任务,idle为线程为闲置状态。Poco中PooledThread继承自Runnable,并且包含一个Thread对象。

class PooledThread: public Runnable
{
public:
	PooledThread(const std::string& name, int stackSize = POCO_THREAD_STACK_SIZE);
	~PooledThread();

	void start();
	void start(Thread::Priority priority, Runnable& target);
	void start(Thread::Priority priority, Runnable& target, const std::string& name);
	bool idle();
	int idleTime();
	void join();
	void activate();
	void release();
	void run();

private:
	volatile bool        _idle;
	volatile std::time_t _idleTime;
	Runnable*            _pTarget;
	std::string          _name;
	Thread               _thread;
	Event                _targetReady;
	Event                _targetCompleted;
	Event                _started;
	FastMutex            _mutex;
};

         对于PooledThread来说,其线程业务就是不断的检测是否有新的外界业务_pTarget,如果有就运行,没有的话,把自己状态标志位限制,供线程池回收。

void PooledThread::run()
{
	_started.set();
	for (;;)
	{
		_targetReady.wait();
		_mutex.lock();
		if (_pTarget) // a NULL target means kill yourself
		{
			_mutex.unlock();
			try
			{
				_pTarget->run();
			}
			catch (Exception& exc)
			{
				ErrorHandler::handle(exc);
			}
			catch (std::exception& exc)
			{
				ErrorHandler::handle(exc);
			}
			catch (...)
			{
				ErrorHandler::handle();
			}
			FastMutex::ScopedLock lock(_mutex);
			_pTarget  = 0;
#if defined(_WIN32_WCE)
			_idleTime = wceex_time(NULL);
#else
			_idleTime = time(NULL);
#endif	
			_idle     = true;
			_targetCompleted.set();
			ThreadLocalStorage::clear();
			_thread.setName(_name);
			_thread.setPriority(Thread::PRIO_NORMAL);
		}
		else
		{
			_mutex.unlock();
			break;
		}
	}
}

         Poco中线程池的实现,耦合性其实是很低的,这不得不归功于其在线程池上两个层次的封装和抽象,类的内聚性非常强的,每个类各干各的事。

3.3 其他

         除了上面线程池的主要属性和接口外,Poco中线程池还实现了一些其他特性。如设置线程运行的优先级,实现了一个默认线程的单件等。

(版权所有,转载时请注明作者和出处 http://blog.csdn.net/arau_sh/article/details/8592579

posted @ 2013-02-19 22:15  在天与地之间  阅读(1221)  评论(0编辑  收藏  举报