c++线程池原理和应用

文章目录

  • 1.线程池
  • 2.线程池作用及应用场合
  • 3. 线程池实例

1.线程池

  • 线程池是在应用程序启动之后,将立即创建一定数量的线程(N1),放入空闲队列中。这些线程都是处于阻塞(Suspended)状态,不消耗CPU,但占用较小的内存空间。当任务到来后,缓冲池选择一个空闲线程,把任务传入此线程中运行。当N1个线程都在处理任务后,缓冲池自动创建一定数量的新线程,用于处理更多的任务。在任务执行完毕后线程也不退出,而是继续保持在池中等待下一次的任务。当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源。

2.线程池作用及应用场合

  • 作用:并发处理数量巨大但相对时间较短的任务,缩短传统线程方案中不停创建线程,销毁线程的时间。

    目前的大多数网络服务器,包括Web服务器、Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,但处理时间却相对较短。
    传统多线程方案中我们采用的服务器模型则是一旦接受到请求之后,即创建一个新的线程,由该线程执行任务。任务执行完毕后,线程退出,这就是是“即时创建,即时销毁”的策略。
    尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态。我们将传统方案中的线程执行过程分为三个过程:
    T1:线程创建时间;
    T2:线程执行时间,包括线程的同步等时间;
    T3:线程销毁时间;
    线程本身的开销所占的比例为(T1+T3) / (T1+T2+T3)。如果线程执行的时间很短的话,这比开销可能占到20%-50%左右。如果任务执行时间很长的话,这笔开销将是不可忽略的。
    线程池能够减少创建的线程个数。通常线程池所允许的并发线程是有上界的,如果同时需要并发的线程数超过上界,那么一部分线程将会等待。而传统方案中,如果同时请求数目为2000,那么最坏情况下,系统可能需要产生2000个线程,机器可能也达不到要求。
    因此线程池的出现正是着眼于减少线程本身带来的开销。基于这种预创建技术,线程池将线程创建和销毁本身所带来的开销分摊到了各个具体的任务上,执行次数越多,每个任务所分担到的线程本身开销则越小,不过我们另外可能需要考虑进去线程之间同步所带来的开销
    事实上,线程池并不是万能的。它有其特定的使用场合。线程池致力于减少线程本身的开销对应用所产生的影响,这是有前提的,前提就是线程本身开销与线程执行任务相比不可忽略。如果线程本身的开销相对于线程任务执行开销而言是可以忽略不计的,那么此时线程池所带来的好处是不明显的,我们可以选择“即时创建,即时销毁”的策略。

线程池通常适合下面的几个场合:
(1) 单位时间内处理任务频繁而且任务处理时间短;
(2) 对实时性要求较高。如果接受到任务后在创建线程,可能满足不了实时要求,因此必须采用线程池进行预创建。

3. 线程池实例

  • 线程池的实现思想:“管理一个任务队列,一个线程队列,然后每次取一个任务分配给一个线程去做,循环往复。”
  • 线程池实现:
#pragma once
#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class ThreadPool {

public:
	ThreadPool(size_t);                          //构造函数
	template             //类模板
	auto enqueue(F&& f, Args&&... args)->std::future::type>;//任务入队
	~ThreadPool();                              //析构函数

private:
	std::vector< std::thread > workers;            //线程队列,每个元素为一个Thread对象
	std::queue< std::function > tasks;     //任务队列,每个元素为一个函数对象    

	std::mutex queue_mutex;                        //互斥量
	std::condition_variable condition;             //条件变量
	bool stop;                                     //停止
};

// 构造函数,把线程插入线程队列,插入时调用embrace_back(),用匿名函数lambda初始化Thread对象
inline ThreadPool::ThreadPool(size_t threads) : stop(false) {

	for (size_t i = 0; i task;
			{
				//首先给互斥量加锁(锁对象生命周期结束后自动解锁) //c++11智能锁,std::unique_lock std::lock_guard
				std::unique_lock lock(this->queue_mutex);

				//获得互斥锁后,调用条件变量的wait()函数等待条件发生,传入wait()函数的第二个参数为一个匿名函数lambda.
				//(1)当匿名函数返回false,wait阻塞线程,(任务队列为空时也会阻塞),阻塞时对互斥对象解锁,自动释放锁。
				//(2)当匿名函数返回true(任务队列不为空)且受到条件变量通知后,waith函数使线程解阻塞,给互斥对象加锁,
				      //接着从任务队列里面弹出一个任务,对互斥量自动解锁,并执行这个任务。
				this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });

				if (this->stop && this->tasks.empty())
					return;

				//从任务队列取出一个任务
				task = std::move(this->tasks.front());
				this->tasks.pop();
			}                            // 自动解锁
			task();                      // 执行这个任务
		}
	}
	);
}

// 添加新的任务到任务队列   (F是func类型)
template
auto ThreadPool::enqueue(F&& f, Args&&... args)
-> std::future::type>
{
	// 获取函数func返回值类型        
	using return_type = typename std::result_of::type;

	// 首先创建一个指向任务的智能指针
	auto task = std::make_shared< std::packaged_task >(
		std::bind(std::forward(f), std::forward(args)...)
		);

	std::future res = task->get_future();
	{
		std::unique_lock lock(queue_mutex);  //对互斥量加锁
		if (stop)
			throw std::runtime_error("enqueue on stopped ThreadPool");

		tasks.emplace([task]() { (*task)(); });          //把任务加入队列
	}                                                   //自动解锁
	condition.notify_one();                             //通知条件变量,使得阻塞线程解阻塞。
return res;
}

// 析构函数,删除所有线程
inline ThreadPool::~ThreadPool()
{
	{
		std::unique_lock lock(queue_mutex);
		stop = true;
	}
	condition.notify_all();
	for (std::thread &worker : workers)
		worker.join();
}

#endif

主程序:

void func()
{
	std::this_thread::sleep_for(std::chrono::milliseconds(100));
	std::cout << "worker thread ID:" << std::this_thread::get_id() << std::endl;
}

int main()
{
	ThreadPool pool(100);
	while (1)
	{
		pool.enqueue(func);
	}
}

输出:

worker thread ID:worker thread ID:12076worker thread ID:7460worker thread ID:6624worker thread ID:7868worker thread ID:13812worker thread ID:13892worker thread ID:7692worker thread ID:1240
worker thread ID:11980worker thread ID:14180worker thread ID:12844worker thread ID:11880worker thread ID:9872worker thread ID:10704worker thread ID:11832worker thread ID:3928worker thread ID:15184worker thread ID:4540
worker thread ID:5208worker thread ID:13808worker thread ID:8748worker thread ID:5076worker thread ID:3676worker thread ID:324worker thread ID:4840worker thread ID:2116worker thread ID:11236worker thread ID:5804worker thread ID:10604worker thread ID:184worker thread ID:1452worker thread ID:11188worker thread ID:8532worker thread ID:7004worker thread ID:624worker thread ID:15076worker thread ID:5880worker thread ID:7196worker thread ID:2832worker thread ID:5276worker thread ID:7388worker thread ID:1784worker thread ID:14908worker thread ID:11288worker thread ID:15124worker thread ID:2836worker thread ID:12276worker thread ID:7084worker thread ID:15168worker thread ID:10004worker thread ID:1744worker thread ID:10880worker thread ID:11732worker thread ID:10140worker thread ID:12752

本文转载自:https://blog.csdn.net/MOU_IT/article/details/88712090

你可能感兴趣的:(C++)