【Linux】浅析进程池与线程池

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41035588/article/details/88678011


池是一种用· 空间换时间的行为,比如我们常见的计算机的 本地缓存代理服务器高速缓存等都是池或池的变种,它有效的避免了 不同设备之间的频繁切换,从而提高了运行效率。

一、池的引入

池是一组资源的集合,有静态、动态资源分配之分。一组资源在服务器启动之初就完全被创建并初始化,称之为静态资源分配。

当服务器运行时,如果需要资源,就可以直接从池中获取,而无需动态分配。因为分配系统资源通常比较耗时,当服务器处理完一个客户连接后,可以将相关资源放回池中,无需调执行系统调用来释放资源,从而避免服务器内核态与用户态的的频繁切换。

二、进程池与线程池

相对于池而言,进程与线程基本上没有区别:在OOP编程中,对象的构造与析构是一个较为复杂的过程,时间消耗较大,为了提高程序的运行效率,要尽可能减少构造和析构的次数。

因此我们要预先存放一些进程或线程到池,用时在池中取,用完归回给池,免去了创建、删除进程的开销。

·进程池中的所有子进程都运行着相同的代码,并具体相同的属性·,比如优先级、PGID等。当有新的任务到来时,调用过程如下:

⑴ 主进程使用某种算法(比如随机算法、轮流算法)来主动选择子进程。

⑵ 主进程和所有子进程通过一个共享的工作队列来同步,子进程睡眠在工作队列上,当有新任务到来时,会唤醒一个正在等待任务的拥有接管权的子进程,它从工作队列中取出任务并执行,而其他子进程将继续睡眠。
  • 线程池的应用

线程池主要用于
1、需要大量的线程来完成任务,且完成任务的时间比较短。

WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。
因为单个任务小,而任务数量巨大,一个热门网站的点击次数会很多。
但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

2、对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

3、接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。

线程池&&进程池的好处
进程池进程池减少了创建,归还的时间。提高了效率。

三、简单线程池的模拟实现


typedef bool (*Handler)(int sock);
class HttpTask
{
//http请求处理的任务
//包含一个成员socket
//包含一个任务处理的接口
private:
	int _cli_sock;  
	Handler TaskHandler;
public:
//设置任务,也可以先初始化,直接调用任务处理接口,通用不强,所以设计如下
	HttpTask():_cli_sock(-1),TaskHandler(NULL){} 
	void SetHttpTask(int sock,Handler handle)
	{
		_cli_sock = sock;
		TaskHandler = handle;
	}
	void Run()
	{
		TaskHandler(_cli_sock);
	}
};

class ThreadPool
{
//创建指定数量的线程
//创建一个线程安全的任务队列
//提供任务的入队,出队,线程池销毁/初始化接口
private:
	int _max_thr;//池的最大线程数量
	int _cur_thr;//当前还有多少线程,确保销毁池的条件
	std::queue<HttpTask> _task_queue;
	pthread_mutex_t _mutex; //保证数据安全
	pthread_cond_t _cond;//保证任务有序
	bool _is_stop;//判断是否终止线程
private:
	static void *thr_start(void* arg)//完成线程获取任务并执行任务,
	{
		pthread_detach(pthread_self());
		ThreadPool* tp = (ThreadPool*)arg;
		tp->QueueLock();

		while(tp->QueueEmpty())
		{
			tp->ThreadWait();
		}
		HttpTask ht;
		tp->PopTaskQueue(ht);
		tp->QueueUnLock();
		ht.Run();
   	return NULL;
	}
	void QueueLock()
	{
		pthread_mutex_lock(&_mutex);
	}
	void QueueUnLock()
	{
		pthread_mutex_unlock(&_mutex);
	}
	
	bool IsStop()
	{
		return _is_stop;
	}
	void ThreadExit()
	{
		_cur_thr--;
		pthread_exit(NULL);
	}
	void ThreadWait()
	{
		if(IsStop())
		{
			//若要销毁,无需等待
			QueueUnLock();
			ThreadExit();	
		}
		pthread_cond_wait(&_cond,&_mutex);
	}
	void ThreadWakeUpOne()
	{
		pthread_cond_signal(&_cond);
	}
	void ThreadWakeUpAll()
	{
		pthread_cond_broadcast(&_cond);
	}
	
	bool QueueEmpty()
	{
		return _task_queue.empty();
	}
	
public:
	
	ThreadPool(int num):_max_thr(num),_cur_thr(0),_is_stop(false){}
	~ThreadPool()
	{
		pthread_mutex_destroy(&_mutex);
		pthread_cond_destroy(&_cond);
	}
	//完成线程创建,互斥锁/条件变量初始化
	bool ThreadPoolInit()
	{
		pthread_t tid;
		for(int i = 0;i<_max_thr;++i)
		{
			int ret = pthread_create(&tid,NULL,thr_start,(void*)this);
			if(ret!=0)
			{
				LOG("create reror: %s\n",strerror(errno));
				return false;
			}

			_cur_thr++;
		}
		
		pthread_mutex_init(&_mutex,NULL);
		pthread_cond_init(&_cond,NULL);
	  return true;
	}
	//线程安全任务入队
	bool PushTaskQueue(HttpTask &tt)
	{
		QueueLock();
		_task_queue.push(tt);
		QueueUnLock();
		ThreadWakeUpOne();
    return true;
	}
	//线程安全任务出队
	bool PopTaskQueue(HttpTask &tt)
	{
		//因为任务出队在线程接口中调用,但是线程接口在出队前会进行加锁,因此此处不需要加锁
		tt = _task_queue.front();
		_task_queue.pop();
	  return true;
  }
	//线程池销毁
	bool ThreadPoolDestroy()
	{
		if(!IsStop())
		{
			_is_stop = true;
		}
		while(_cur_thr>0)
		{
			ThreadWakeUpAll();//唤醒所所有有任务执行,无任务退出
			usleep(1000);
		}
		return true;
	}
};

参考博客:
进程池与线程池

猜你喜欢

转载自blog.csdn.net/qq_41035588/article/details/88678011
今日推荐