C++—线程池(附源代码)

C++—线程池

1、池

什么叫池?
它是预先从系统中申请一大堆的资源,然后使用完成后不还给操作系统,然后保持复用。

有什么好处?
从应用层向操作系统去申请资源(内存,线程)需要从应用层切入内核,分配完成后再切换回应用层使用。

这一大堆资源是内存就是内存池,是线程就是线程池。

什么时候用池?

频繁使用、释放,且资源占用不大的地方改成池操作。

使用时间很短,但是使用的频率很高。

2、预备知识

2.1 为什么要使用智能指针 (防止内存泄露)

上述代码中的x,y在程序运行结束后会自动释放,但是p是使用new在堆上面分配的,如果不使用delete释放内存,那么这块指向这段内存的指针p会随着函数的结束而消失,这块内存无人问津,即内存泄露。

2.2 智能指针类型

在现代 C++ 编程中,智能指针是管理动态内存和资源的关键工具。<memory> 头文件提供了几种智能指针,包括 std::unique_ptr、std::shared_ptr 和 std::weak_ptr。每种智能指针有其独特的用途和特点。

2.2.1 std::unique_ptr

std::unique_ptr 是一种独享的智能指针(一个指针只能指向一段内存),它负责管理一个动态分配的对象的生命周期,并保证在 std::unique_ptr 被销毁时,所管理的对象会被正确释放。std::unique_ptr 不能被复制,但可以被移动。

 这段代码前5行代码是按照函数运行顺序进行的,但是在func函数运行结束后,自动执行了Ball类的析构函数打印了A ball disappears。避免了内存的泄露。

2.2.2 std::shared_ptr

std::shared_ptr 是一种共享的智能指针,它允许多个 std::shared_ptr 实例共享对同一个动态分配对象的所有权。对象的生命周期由引用计数控制,当最后一个 std::shared_ptr 被销毁时,对象才会被释放。

建议使用make_shared创建智能指针。 

那是如何管理内存的?

 resent会重置智能指针,不再指向原来物体,计数归0,自动释放内存。

2.2.3 std::weak_ptr(弱指针)

std::weak_ptr 是一种弱引用智能指针,它不影响对象的引用计数。std::weak_ptr 主要用于解决 std::shared_ptr 的循环引用问题。通过 std::weak_ptr 可以安全地观察对象(检查数据是否存在),但不控制对象的生命周期。

只能告诉你资源是否存在,就算访问数据也要借助std::shared_ptr(lock方法)进行访问。

2.3 原子操作、锁和CAS

<atomic> 头文件提供了原子操作功能,在并发编程中尤为重要。原子操作是不可分割的操作,这意味着在执行期间不会被中断。在多线程环境中,原子操作可以用来安全地操作共享数据,而无需使用传统的锁机制(如互斥锁),从而提高性能和减少复杂性。

这两条指令只有一条会成功。 另外一条会被CPU拒绝。

使用CAS(比较和更新,CPU将其称为原子操作)

 CAS代表了一个线程失败,另外一个线程就会成功。

假设我们将以下两个线程整合为原子操作。

 

如何解决这个问题呢? 给线程上锁上锁(lock)

 

lock与cas loop(cas循环)的区别?

主要组件
std::atomic:原子类型模板,用于定义可以在多线程环境下安全访问的变量。
std::atomic_flag:最基本的原子类型,用于实现锁的功能。
原子操作函数:

如 load(), store(), exchange(), compare_exchange_weak(),compare_exchange_strong() 等,用于执行原子操作。


2.3.1. std::atomic


std::atomic 是一个模板类,用于创建原子类型的变量。它提供了一系列原子操作函数,可以保证操作的原子性和线程安全性。以下是一些常用的操作函数:

load():以原子方式读取值。
store(value):以原子方式设置值。
exchange(value):以原子方式设置值,并返回旧值。
compare_exchange_weak(expected, desired):以原子方式比较当前值与期望值,如果相等则设置为新值,否则更新期望值为当前值。
compare_exchange_strong(expected, desired):与 compare_exchange_weak 类似,但提供更强的保证,适用于复杂的比较和交换操作。

2.3.2 std::atomic_flag


std::atomic_flag 是一种简单的原子类型,通常用于实现低级别的锁机制。它只有两个操作:test_and_set() 和 clear()。test_and_set() 会设置标志位并返回旧值,而 clear() 会清除标志位。

2.3.3. 内存序 (Memory Order)


原子操作还涉及内存序,确定操作的可见性和顺序。主要有以下几种内存序:

std::memory_order_relaxed:最宽松的内存序,不保证任何顺序。
std::memory_order_consume:保证操作依赖关系的顺序,但对性能影响较大。
std::memory_order_acquire:保证操作之前的所有操作都在当前操作之前完成。
std::memory_order_release:保证当前操作之前的所有操作都在当前操作之后完成。
std::memory_order_acq_rel:结合 acquire 和 release。
std::memory_order_seq_cst:最严格的内存序,保证所有操作的全局顺序。

总结


std::atomic 提供了原子操作,适用于需要在多线程环境中进行无锁操作的场景。
std::atomic_flag 用于实现低级别的锁机制,如自旋锁。
了解内存序是使用原子操作时的一个重要方面,它帮助你控制操作的顺序和可见性,从而确保程序的正确性和效率。

2.4 并发与并行

一般情况下,两种情况统称为并发程序。面试如果问到区别,可以这样描述。

 并发程序相当于单核CPU中执行任务时,多个任务本身是串行的,但是由于每个任务执行时间都很短,可以看做是同时执行的,这种称为并发。而并行程序是真正的多个任务同时进行,在多核CPU中,可以同时进行多个任务。

 2.5 多线程的优势

具体描述:多线程程序设计不一定是好的,得分情况。在IO密集型中,使用多线程是好的,因为IO操作是可以把程序阻塞住的,这就导致CPU空闲了,浪费资源。而cpu密集型

 

 2.6 多线程的消耗?

 

2.7 线程池

2.7.1 线程的优势

2.7.2 fixed模式线程池

2.7.3 cached模式线程池

2.8 线程同步 

2.8.1 线程互斥

概念:是一种同步机制,用来防止多个线程同时访问共享资源,从而避免竞争条件

互斥锁mutex

atomic原子操作

//使用锁是为了保证原子操作,避免竞态条件。

总结:竞态条件:代码片段在多线程环境下执行,随着线程的调动顺序不同,而得到不同的使用结果。这种情况是不行的。存在竞态条件这段代码称为临界区代码段。

线程互斥是为了避免竞争条件的。分类互斥锁和原子操作。

如果在多线程环境下是不存在竞态条件的===》可重入的

存在竞态条件的===》不可重入的!则需要使用原子操作,线程互斥。

使用互斥锁(lock或者trylock)。互斥锁使得一个线程接收了任务,其他线程不在接收。互斥锁保证了只有一个线程能够进入临界区代码段。

(lock和trylock(活锁)的区别:lock:用于阻塞获取锁,适合需要严格确保独占访问的场景。
trylock:用于非阻塞尝试获取锁,允许线程有其他选择的场景。)

CAS操作(无锁机制)无锁队列、无锁链表、无锁数组。

 一般情况下,在C++11环境下,都不使用这种lock和unlock的情景,因为你写了unlock和delete一样,写了也不一定能运行到,会造成内存泄露。因此使用智能锁。lock_guard(和智能指针一样,在构造的时候加锁,在析构的时候解锁)和unique_lock(提供了手动上锁和解锁的操作,可以和条件变量一起操作。)

2.8.2 线程通信(生产者消费者模型)

条件变量 condition_variable

信号量 semaphone(C++20信号量)

主要包括条件变量应用和信号量应用。

线程A代码段的执行要依赖于线程B代码段的执行结束,因此就涉及到线程的通信了。 就算线程A先进入执行,也需要让线程A进入等待,等B运行结束再执行。(条件变量和信号量都可以执行)

1、条件变量 condition_variable一般结合unique_lock一起使用,

在生产者消费者模型中,如生产者一直占着锁,但是容器此时以及已经满了,那麽就需要让生产者线程等待。wait总共做了两件事情,1、改变了线程的状态(等待)2、把互斥锁释放了.

如果容器不满,抢占锁。

总结:

1、共享内存:线程可以通过共享内存区域交换数据,例如全局变量或静态变量。然而,使用共享内存需要适当的同步机制来避免竞争条件。

2、同步机制:互斥锁(Mutex):用于保护共享资源,确保同一时间只有一个线程能够访问资源。条件变量(Condition Variable):允许线程在特定条件下进行等待和通知,用于协调线程之间的执行顺序。
3、线程间通信的模式:生产者-消费者模式:一个线程生产数据并将其放入缓冲区,另一个线程从缓冲区取出数据进行处理。
发布-订阅模式:一个线程发布消息,多个订阅线程接收并处理这些消息。

2、信号量(semaphore)

mutex的资源计数是0或者1.而semphone的资源计数可以是任意的。

总结:信号量semaphore和互斥锁的区别?

互斥锁是做线程互斥的,信号量是做线程通信的。其次,mutex只能是哪个线程获取锁,由哪个线程释放锁!sem不同,sem.wait()和sem.post()可以处在不同的线程中调用。

PV操作:P代表sem.wait()等待,V代表sem.post()是信号量资源加1.而对于条件变量使用cond.waut()代表P操作,cond.notify代表V操作。

3、整体架构梳理

使用方法:

框架图: 

4、源代码

(博主正在敲写中,敲完会上传到github,敬请期待)

4.1 ThreadPool类实现

#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <vector>
#include<queue>
#include<memory>
#include<atomic>
#include<mutex>
#include<condition_variable>

//任务抽象基类
class Task
{
public:
	//用户可以自定义任意任务类型,从Task继承,重写run方法,实现自定义任务处理
	virtual void run() = 0;
};
//线程池支持的模式
enum class poolMode //enum class是为了解决枚举不同,但是枚举项相同的情况
{
	MODE_FIXED,  //固定数量的线程
	MODE_CACHED,  //线程数量可动态可增长
};
//线程类型
class Thread
{

};
//线程池类型
class ThreadPool
{
public:
	ThreadPool(); 
	~ThreadPool();
	
	//设置线程池的工作模式
	void setMode(poolMode mode);

	//设置task任务队列的上限阈值
	void setTaskQueMaxThreshHold(int threshhold);

	//给线程池提交任务
	void submitTask(std::shared_ptr<Task>sp);  //因为任务周期不知道,因此传一个智能指针

	//开启线程池
	void start();
private:
	std::vector<Thread*>threads_;//threads_  线程列表
	size_t initThreadSize_;  //初始的线程数量

	std::queue<std::shared_ptr<Task>>taskQue_;  //任务队列
	std::atomic_int taskSize_;  //任务数量
	int taskQueMaxThreshHold_;  //任务队列数量上限阈值

	std::mutex taskQueMtx_;     //保证任务队列的线程安全  互斥锁
	std::condition_variable notFull_;
	std::condition_variable notEmpty_;  //表示任务队列不空

	poolMode poolMode_;  //当前线程池的工作模式
};

#endif

4.2 ThreadPool方法接口实现以及线程函数的实现

threadpool.cpp文件

#include "threadpool.h"
#include<functional>
#include<thread>
#include<iostream>

const int TASK_MAX_THRESHHOLD = 1024;


//线程池构造
ThreadPool::ThreadPool()
	:initThreadSize_(4)
	, taskSize_(0)
	, taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
	, poolMode_(PoolMode::MODE_FIXED)
{}

//线程池析构
ThreadPool::~ThreadPool()
{}

//设置线程池的工作模式
void ThreadPool::setMode(PoolMode mode)
{
	poolMode_ = mode;
}

//设置task任务队列的上限阈值
void ThreadPool::setTaskQueMaxThreshHold(int threshhold)
{
	taskQueMaxThreshHold_ = threshhold;
}

//给线程池提交任务  用户调用接口 传入任务 生产任务
void ThreadPool::submitTask(std::shared_ptr<Task>sp)  //因为任务周期不知道,因此传一个智能指针
{

}
//开启线程池
void ThreadPool::start(int initThreadSize)
{
	//记录初始线程个数
	initThreadSize_ = initThreadSize;

	//创建线程对象
	for (int i = 0; i < initThreadSize_; i++)
	{
		//创建thread线程对象的时候,把threadpool的线程函数给到thread对象   //绑定
		threads_.emplace_back(new Thread(std::bind(&ThreadPool::threadFunc,this)));
	}

	//启动所有线程
	for (int i = 0; i < initThreadSize_; i++)
	{
		threads_[i]->start();
	}
}

//定义线程函数  所有线程函数是从任务队列中消费任务
void ThreadPool::threadFunc()
{
	std::cout << "begin treadfunc tid:" << std::this_thread::get_id() 
	<< std::endl;
	
	std::cout << "end treadfunc tid:" << std::this_thread::get_id() 
	<<std::endl;
}

  线程方法实现

//线程构造
Thread::Thread(ThreadFunc func)
	:func_(func)
{

}
//线程析构
Thread::~Thread()  
{

}

//启动线程
void Thread::start()
{
	//执行一个线程函数
	std::thread t(func_);         //C++11的线程类  线程对象t 和线程函数func_
	t.detach();  //设置分离线程   将线程对象和线程函数进行分离,即在启动线程这个函数结束后,线程对象消失就消失了,不影响函数的运行
}

threadpool.h文件

#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <vector>
#include<queue>
#include<memory>
#include<atomic>
#include<mutex>
#include<condition_variable>
#include<functional>

//任务抽象基类
class Task
{
public:
	//用户可以自定义任意任务类型,从Task继承,重写run方法,实现自定义任务处理
	virtual void run() = 0;
};
//线程池支持的模式
enum class PoolMode //enum class是为了解决枚举不同,但是枚举项相同的情况
{
	MODE_FIXED,  //固定数量的线程
	MODE_CACHED,  //线程数量可动态可增长
};
//线程类型
class Thread
{
public:
	
	//线程函数对象类型
	using ThreadFunc = std::function<void()>;

	//线程构造
	Thread(ThreadFunc func);
	//线程析构
	~Thread();
	//启动线程
	void start();
private:
	ThreadFunc func_;
};

//线程池类型
class ThreadPool
{
public:
	//线程池构造
	ThreadPool();
	//线程池析构
	~ThreadPool();
	
	//设置线程池的工作模式
	void setMode(PoolMode mode);

	//设置task任务队列的上限阈值
	void setTaskQueMaxThreshHold(int threshhold);

	//给线程池提交任务
	void submitTask(std::shared_ptr<Task>sp);  //因为任务周期不知道,因此传一个智能指针

	//开启线程池
	void start(int initThreadSize=4);

	//禁止拷贝构造和拷贝赋值操作,用户使用线程池可以单独创建一个线程池对象,对线程池本身不能操作
	ThreadPool(const ThreadPool&) = delete;
	ThreadPool& operator=(const ThreadPool&) = delete;  
private:
	//定义线程函数
	void threadFunc();
private:
	std::vector<Thread*>threads_;//threads_  线程列表
	size_t initThreadSize_;  //初始的线程数量

	std::queue<std::shared_ptr<Task>>taskQue_;  //任务队列
	std::atomic_int taskSize_;  //任务数量
	int taskQueMaxThreshHold_;  //任务队列数量上限阈值

	std::mutex taskQueMtx_;     //保证任务队列的线程安全  互斥锁
	std::condition_variable notFull_;
	std::condition_variable notEmpty_;  //表示任务队列不空

	PoolMode poolMode_;  //当前线程池的工作模式
};

#endif

测试文件:

#include<iostream>
#include<chrono>
#include<thread>
#include"threadpool.h"
int main()
{
	ThreadPool pool;
	pool.start(6);

	//主线程结束后,分离线程给自动回收了,分离线程可能还没启动
	std::this_thread::sleep_for(std::chrono::seconds(5));  //睡眠函数,主线程睡眠5s
}

测试结果:

碰到的主要问题:

//启动线程
void Thread::start()
{
    //执行一个线程函数
    std::thread t(func_);         //C++11的线程类  线程对象t 和线程函数func_
    t.detach();  //设置分离线程   将线程对象和线程函数进行分离,即在启动线程这个函数结束后,线程对象消失就消失了,不影响函数的运行
}

启动函数函数中设定了分离线程,但是测试阶端分离线程还没启动主线程已经结束了,测试没有输出?

解决方案:使用了睡眠函数,使得主线程睡眠一段时间,等待分离线程完成。

4.3 submitTask给线程池提交任务代码输出

//给线程池提交任务  用户调用接口 传入任务 生产任务
void ThreadPool::submitTask(std::shared_ptr<Task>sp)  //因为任务周期不知道,因此传一个智能指针
{
	//获取锁
	std::unique_lock<std::mutex>lock(taskQueMtx_);
	
	//线程的通信  等待任务队列有空余
	/*while (taskQue_.size() == taskQueMaxThreshHold_)  //与lamda表达式意义相同
	{
		notFull_.wait(lock);
	}*/
	notFull_.wait(lock, [&]()->bool {return taskQue_.size() < taskQueMaxThreshHold_; });  //lamda表达式

	//如果有空余,把任务放入任务队列中
	taskQue_.emplace(sp);
	taskSize_++;

	//因为新放了任务,任务队列肯定不空了,在notEmpty_上进行通知,赶快分配线程执行任务
	notEmpty_.notify_all();
}

4.4 代码功能测试

threadpool.cpp文件

#include "threadpool.h"
#include<functional>
#include<thread>
#include<iostream>

const int TASK_MAX_THRESHHOLD = 1024;


//线程池构造
ThreadPool::ThreadPool()
	:initThreadSize_(4)
	, taskSize_(0)
	, taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
	, poolMode_(PoolMode::MODE_FIXED)
{}

//线程池析构
ThreadPool::~ThreadPool()
{}

//设置线程池的工作模式
void ThreadPool::setMode(PoolMode mode)
{
	poolMode_ = mode;
}

//设置task任务队列的上限阈值
void ThreadPool::setTaskQueMaxThreshHold(int threshhold)
{
	taskQueMaxThreshHold_ = threshhold;
}

//给线程池提交任务  用户调用接口 传入任务 生产任务
void ThreadPool::submitTask(std::shared_ptr<Task>sp)  //因为任务周期不知道,因此传一个智能指针
{
	//获取锁
	std::unique_lock<std::mutex>lock(taskQueMtx_);
	
	//线程的通信  等待任务队列有空余
	/*while (taskQue_.size() == taskQueMaxThreshHold_)  //与lamda表达式意义相同
	{
		notFull_.wait(lock);
	}*/
	//用户提交任务,最长不会阻塞1s,否则判断提交任务失败,返回
	if(!notFull_.wait_for(lock,std::chrono::seconds(1),
		[&]()->bool {return taskQue_.size() < (size_t)taskQueMaxThreshHold_; })) //lamda表达式
	{
		//表示notfull_等待1s,条件依然没有满足
		std::cerr << "task queue in full,submit task fail." << std::endl;
		return;
	}
	//如果有空余,把任务放入任务队列中
	taskQue_.emplace(sp);
	taskSize_++;

	//因为新放了任务,任务队列肯定不空了,在notEmpty_上进行通知,赶快分配线程执行任务
	notEmpty_.notify_all();
}
//开启线程池
void ThreadPool::start(int initThreadSize)
{
	//记录初始线程个数
	initThreadSize_ = initThreadSize;

	//创建线程对象
	for (int i = 0; i < (int)initThreadSize_; i++)
	{
		//创建thread线程对象的时候,把threadpool的线程函数给到thread对象   //绑定
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this));
		//threads_.emplace_back(ptr);  //unique_ptr不允许拷贝构造
		threads_.emplace_back(std::move(ptr));
	}

	//启动所有线程
	for (int i = 0; i < (int)initThreadSize_; i++)
	{
		threads_[i]->start();
	}
}

//定义线程函数  所有线程函数是从任务队列中消费任务
void ThreadPool::threadFunc()
{
	/*std::cout << "begin treadfunc tid:" << std::this_thread::get_id() 
	<< std::endl;
	
	std::cout << "end treadfunc tid:" << std::this_thread::get_id() 
	<<std::endl;*/
	for (;;)
	{
		std::shared_ptr<Task> task;
		{
		//先获取锁
		std::unique_lock<std::mutex>lock(taskQueMtx_);
		
		std::cout << "tid:" << std::this_thread::get_id()
			<< "尝试获取任务" << std::endl;
		//等待notEmpty条件
		notEmpty_.wait(lock, [&]()->bool {return taskQue_.size() > 0; });

		std::cout << "tid:" << std::this_thread::get_id()
			<< "获取任务成功" << std::endl;
		//从任务队列中取一个任务出来
		task = taskQue_.front();
		taskQue_.pop();
		taskSize_--;

		//如果依然有剩余任务,继续通知其他的线程执行任务
		if (taskQue_.size() > 0)
		{
			notEmpty_.notify_all();
		}
		//取出一个任务,进行通知,通知可以继续提交生产任务
		notFull_.notify_all();
		}//就应该把锁释放掉

		//当前线程负责执行这个任务
		if (task != nullptr)
		{
			task->run();
		}
	}
}

  线程方法实现

//线程构造
Thread::Thread(ThreadFunc func)
	:func_(func)
{

}
//线程析构
Thread::~Thread()  
{

}

//启动线程
void Thread::start()
{
	//执行一个线程函数
	std::thread t(func_);         //C++11的线程类  线程对象t 和线程函数func_
	t.detach();  //设置分离线程   将线程对象和线程函数进行分离,即在启动线程这个函数结束后,线程对象消失就消失了,不影响函数的运行
}

 threadpool.h文件

#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <vector>
#include<queue>
#include<memory>
#include<atomic>
#include<mutex>
#include<condition_variable>
#include<functional>

//任务抽象基类
class Task
{
public:
	//用户可以自定义任意任务类型,从Task继承,重写run方法,实现自定义任务处理
	virtual void run() = 0;
};
//线程池支持的模式
enum class PoolMode //enum class是为了解决枚举不同,但是枚举项相同的情况
{
	MODE_FIXED,  //固定数量的线程
	MODE_CACHED,  //线程数量可动态可增长
};
//线程类型
class Thread
{
public:
	
	//线程函数对象类型
	using ThreadFunc = std::function<void()>;

	//线程构造
	Thread(ThreadFunc func);
	//线程析构
	~Thread();
	//启动线程
	void start();
private:
	ThreadFunc func_;
};

/*
example:
pool.start(4);

class Mytask:public Task
{
   public:
        void run(){//线程代码...}
};
    pool.submitTask(std::make_shared<Mytask>());
*/

//线程池类型
class ThreadPool
{
public:
	//线程池构造
	ThreadPool();
	//线程池析构
	~ThreadPool();
	
	//设置线程池的工作模式
	void setMode(PoolMode mode);

	//设置task任务队列的上限阈值
	void setTaskQueMaxThreshHold(int threshhold);

	//给线程池提交任务
	void submitTask(std::shared_ptr<Task>sp);  //因为任务周期不知道,因此传一个智能指针

	//开启线程池
	void start(int initThreadSize=4);

	//禁止拷贝构造和拷贝赋值操作,用户使用线程池可以单独创建一个线程池对象,对线程池本身不能操作
	ThreadPool(const ThreadPool&) = delete;
	ThreadPool& operator=(const ThreadPool&) = delete;  
private:
	//定义线程函数
	void threadFunc();
private:
	std::vector<std::unique_ptr<Thread>>threads_;//threads_  线程列表
	size_t initThreadSize_;  //初始的线程数量

	std::queue<std::shared_ptr<Task>>taskQue_;  //任务队列
	std::atomic_int taskSize_;  //任务数量
	int taskQueMaxThreshHold_;  //任务队列数量上限阈值

	std::mutex taskQueMtx_;     //保证任务队列的线程安全  互斥锁
	std::condition_variable notFull_;
	std::condition_variable notEmpty_;  //表示任务队列不空

	PoolMode poolMode_;  //当前线程池的工作模式
};

#endif

测试文件:

#include<iostream>
#include<chrono>
#include<thread>
#include"threadpool.h"


class MyTask :public Task
{
public:
	void run()
	{
		std::cout << "tid:" << std::this_thread::get_id()
	        <<"begin!" << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(2));
		std::cout << "tid:" << std::this_thread::get_id()
			<<"end!" << std::endl;
	}
};
int main()
{
	ThreadPool pool;
	pool.start(4);

	pool.submitTask(std::make_shared<MyTask>());
	pool.submitTask(std::make_shared<MyTask>());
	pool.submitTask(std::make_shared<MyTask>());

	getchar();
	//主线程结束后,分离线程给自动回收了,分离线程可能还没启动
	//std::this_thread::sleep_for(std::chrono::seconds(5));  //睡眠函数,主线程睡眠5s
}

测试结果:

4.5 any类型 

//Any类型:可以接收任意数据的类型
class Any
{
public:
	Any() = default;
	~Any() = default;
	Any(const Any&) = delete;
	Any(Any&&) = default;
	Any& operator=(Any&&) = default;

	template<typename T>
	Any(T data) :base_(std::make_unique<Derive<T>>(data))
	{}
private:
	//基类类型
	class Base
	{
	public:
		virtual ~Base()=default;
	};

	//派生类类型
	template<typename T>
	class Derive :public Base
	{
	public:
		Derive(T data) :data_(data)
		{}
	private:
		T data_;
	};

private:
	//定义一个基类指针
	std::unique_ptr<Base>base_;
};

 4.5 Semaphone类型 

//实现一个信号量类
class Semaphore
{
public:
	Semaphore(int limit = 0)
		:resLimit_(limit)
	{}
	~Semaphore() = default;
	
	//获取一个信号量资源
	void wait()
	{
		std::unique_lock<std::mutex>lock(mtx_);
		//等待信号量有资源,没有资源的话,会阻塞当前线程
		cond_.wait(lock, [&]()->bool {return resLimit_ > 0; });
		resLimit_--;
	}
	//增加一个信号量资源
	void post()
	{
		std::unique_lock<std::mutex>lock(mtx_);
		resLimit_++;
		cond_.notify_all();
	}
private:
	int resLimit_;
    std::mutex mtx_;
	std::condition_variable cond_;
};

 4.6 固定Fixed模式线程池代码如下:

cpp文件:

#include "threadpool.h"
#include<functional>
#include<thread>
#include<iostream>

const int TASK_MAX_THRESHHOLD = 1024;


//线程池构造
ThreadPool::ThreadPool()
	:initThreadSize_(4)
	, taskSize_(0)
	, taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
	, poolMode_(PoolMode::MODE_FIXED)
{}

//线程池析构
ThreadPool::~ThreadPool()
{}

//设置线程池的工作模式
void ThreadPool::setMode(PoolMode mode)
{
	poolMode_ = mode;
}

//设置task任务队列的上限阈值
void ThreadPool::setTaskQueMaxThreshHold(int threshhold)
{
	taskQueMaxThreshHold_ = threshhold;
}

//给线程池提交任务  用户调用接口 传入任务 生产任务
Result ThreadPool::submitTask(std::shared_ptr<Task>sp)  //因为任务周期不知道,因此传一个智能指针
{
	//获取锁
	std::unique_lock<std::mutex>lock(taskQueMtx_);
	
	//线程的通信  等待任务队列有空余
	/*while (taskQue_.size() == taskQueMaxThreshHold_)  //与lamda表达式意义相同
	{
		notFull_.wait(lock);
	}*/
	//用户提交任务,最长不会阻塞1s,否则判断提交任务失败,返回
	if(!notFull_.wait_for(lock,std::chrono::seconds(1),
		[&]()->bool {return taskQue_.size() < (size_t)taskQueMaxThreshHold_; })) //lamda表达式
	{
		//表示notfull_等待1s,条件依然没有满足
		std::cerr << "task queue in full,submit task fail." << std::endl;
		return Result(sp,false);
	}
	//如果有空余,把任务放入任务队列中
	taskQue_.emplace(sp);
	taskSize_++;

	//因为新放了任务,任务队列肯定不空了,在notEmpty_上进行通知,赶快分配线程执行任务
	notEmpty_.notify_all();

	//返回任务的Result对象
	return Result(sp);
}
//开启线程池
void ThreadPool::start(int initThreadSize)
{
	//记录初始线程个数
	initThreadSize_ = initThreadSize;

	//创建线程对象
	for (int i = 0; i < (int)initThreadSize_; i++)
	{
		//创建thread线程对象的时候,把threadpool的线程函数给到thread对象   //绑定
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this));
		//threads_.emplace_back(ptr);  //unique_ptr不允许拷贝构造
		threads_.emplace_back(std::move(ptr));
	}

	//启动所有线程
	for (int i = 0; i < (int)initThreadSize_; i++)
	{
		threads_[i]->start();
	}
}

//定义线程函数  所有线程函数是从任务队列中消费任务
void ThreadPool::threadFunc()
{
	/*std::cout << "begin treadfunc tid:" << std::this_thread::get_id() 
	<< std::endl;
	
	std::cout << "end treadfunc tid:" << std::this_thread::get_id() 
	<<std::endl;*/
	for (;;)
	{
		std::shared_ptr<Task> task;
		{
		//先获取锁
		std::unique_lock<std::mutex>lock(taskQueMtx_);
		
		std::cout << "tid:" << std::this_thread::get_id()
			<< "尝试获取任务" << std::endl;
		//等待notEmpty条件
		notEmpty_.wait(lock, [&]()->bool {return taskQue_.size() > 0; });

		std::cout << "tid:" << std::this_thread::get_id()
			<< "获取任务成功" << std::endl;
		//从任务队列中取一个任务出来
		task = taskQue_.front();
		taskQue_.pop();
		taskSize_--;

		//如果依然有剩余任务,继续通知其他的线程执行任务
		if (taskQue_.size() > 0)
		{
			notEmpty_.notify_all();
		}
		//取出一个任务,进行通知,通知可以继续提交生产任务
		notFull_.notify_all();
		}//就应该把锁释放掉

		//当前线程负责执行这个任务
		if (task != nullptr)
		{
			//task->run();  //执行任务; 把任务的返回值setVal方法给到Result
			task->exec();  
		}
	}
}

  线程方法实现

//线程构造
Thread::Thread(ThreadFunc func)
	:func_(func)
{

}
//线程析构
Thread::~Thread()  
{

}

//启动线程
void Thread::start()
{
	//执行一个线程函数
	std::thread t(func_);         //C++11的线程类  线程对象t 和线程函数func_
	t.detach();  //设置分离线程   将线程对象和线程函数进行分离,即在启动线程这个函数结束后,线程对象消失就消失了,不影响函数的运行
}


///Task方法实现
Task::Task()
	:result_(nullptr)
{

}
void Task::exec()
{
	if (result_ != nullptr)
	{
		result_->setVal(run());  //这里发生多态调用
	}
}
void Task::setResult(Result* res)
{
	result_ = res;
}
//  Result方法的实现
Result::Result(std::shared_ptr<Task> task, bool isValid)
	:isValid_(isValid)
	, task_(task)
{
	task->setResult(this);
}
Any Result::get()  //用户调用的
{
	if (!isValid_)
	{
		return"";
	}

	sem_.wait();  //task任务如果没有执行完,这里会阻塞用户的线程
	return std::move(any_);
}

void Result::setVal(Any any)  //谁调用的
{
	//存储task的返回值
	this->any_ = std::move(any);
	sem_.post();  //已经获取的任务的返回值,增加信号量资源
}

.h文件

#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <vector>
#include<queue>
#include<memory>
#include<atomic>
#include<mutex>
#include<condition_variable>
#include<functional>


//Any类型:可以接收任意数据的类型
class Any
{
public:
	Any() = default;
	~Any() = default;
	Any(const Any&) = delete;
	Any& operator=(const Any&) = delete;
	Any(Any&&) = default;
	Any& operator=(Any&&) = default;
	
	//这个构造函数可以让Any类型接收任意其他的数据
	template<typename T>
	Any(T data) :base_(std::make_unique<Derive<T>>(data))
	{}

	//这个方法能把Any对象里面存储的data数据提取出来
	template<typename T>
	T cast_()
	{
		//我们怎么从base_找到它所指向的Derive对象,从它里面提取出data成员变量
		//基类指针=》派生类指针  RTTI
		Derive<T>* pd = dynamic_cast<Derive<T>*>(base_.get());
		if (pd == nullptr)
		{
			throw "type is unmatch!";
		}
		return pd->data_;
	}
private:
	//基类类型
	class Base
	{
	public:
		virtual ~Base()=default;
	};

	//派生类类型
	template<typename T>
	class Derive :public Base
	{
	public:
		Derive(T data) :data_(data)
		{}
		T data_;  //保存了任意的其他类型
	};

private:
	//定义一个基类指针
	std::unique_ptr<Base>base_;
};

//实现一个信号量类
class Semaphore
{
public:
	Semaphore(int limit = 0)
		:resLimit_(limit)
	{}
	~Semaphore() = default;
	
	//获取一个信号量资源
	void wait()
	{
		std::unique_lock<std::mutex>lock(mtx_);
		//等待信号量有资源,没有资源的话,会阻塞当前线程
		cond_.wait(lock, [&]()->bool {return resLimit_ > 0; });
		resLimit_--;
	}
	//增加一个信号量资源
	void post()
	{
		std::unique_lock<std::mutex>lock(mtx_);
		resLimit_++;
		cond_.notify_all();
	}
private:
	int resLimit_;
    std::mutex mtx_;
	std::condition_variable cond_;
};

//Task类型的前置声明
class Task;

//实现接收提交到的线程池的task任务执行完成后的返回值类型Resulit
class Result
{
public:
	Result(std::shared_ptr<Task> task, bool isValid = true);
	~Result() = default;

	//问题1 :setVal方法,获取任务执行完的返回值的
	void setVal(Any any);

	//问题2:get方法,用户调用这个方法获取task的返回值
	Any get();

private:
	Any any_;  //存储任务的返回值
	Semaphore sem_;  //线程通信信号量
	std::shared_ptr<Task>task_;  //指向对应获取返回值的任务对象
	std::atomic_bool isValid_;   //返回值是否有效
};
//任务抽象基类
class Task
{
public:
	Task();
	~Task() = default;
	void exec();
	void setResult(Result* res);
	//用户可以自定义任意任务类型,从Task继承,重写run方法,实现自定义任务处理
	virtual Any run() = 0;
private:
	Result* result_;  //Result对象的声明周期》》Task的
};
//线程池支持的模式
enum class PoolMode //enum class是为了解决枚举不同,但是枚举项相同的情况
{
	MODE_FIXED,  //固定数量的线程
	MODE_CACHED,  //线程数量可动态可增长
};
//线程类型
class Thread
{
public:
	
	//线程函数对象类型
	using ThreadFunc = std::function<void()>;

	//线程构造
	Thread(ThreadFunc func);
	//线程析构
	~Thread();
	//启动线程
	void start();
private:
	ThreadFunc func_;
};

/*
example:
pool.start(4);

class Mytask:public Task
{
   public:
        void run(){//线程代码...}
};
    pool.submitTask(std::make_shared<Mytask>());
*/

//线程池类型
class ThreadPool
{
public:
	//线程池构造
	ThreadPool();
	//线程池析构
	~ThreadPool();
	
	//设置线程池的工作模式
	void setMode(PoolMode mode);

	//设置task任务队列的上限阈值
	void setTaskQueMaxThreshHold(int threshhold);

	//给线程池提交任务
	Result submitTask(std::shared_ptr<Task>sp);  //因为任务周期不知道,因此传一个智能指针

	//开启线程池
	void start(int initThreadSize=4);

	//禁止拷贝构造和拷贝赋值操作,用户使用线程池可以单独创建一个线程池对象,对线程池本身不能操作
	ThreadPool(const ThreadPool&) = delete;
	ThreadPool& operator=(const ThreadPool&) = delete;  
private:
	//定义线程函数
	void threadFunc();
private:
	std::vector<std::unique_ptr<Thread>>threads_;//threads_  线程列表
	size_t initThreadSize_;  //初始的线程数量

	std::queue<std::shared_ptr<Task>>taskQue_;  //任务队列
	std::atomic_int taskSize_;  //任务数量
	int taskQueMaxThreshHold_;  //任务队列数量上限阈值

	std::mutex taskQueMtx_;     //保证任务队列的线程安全  互斥锁
	std::condition_variable notFull_;
	std::condition_variable notEmpty_;  //表示任务队列不空

	PoolMode poolMode_;  //当前线程池的工作模式
};

#endif

测试文件:

#include<iostream>
#include<chrono>
#include<thread>
#include"threadpool.h"
//有些场景,是希望能够获取线程执行任务得到返回值的
//举例:
//1+。。。+3000的和
//thread1 1+..。+1000
//thread1 1000 + ... + 3000

using uLong = unsigned long long;
class MyTask :public Task
{
public:
	MyTask(uLong begin, uLong end)
		:begin_(begin)
		, end_(end)
	{}
	//问题1:怎么设计run函数的返回值,可以表示任意的类型
	Any run()  //run函数在线程池分配的线程中去做事情
	{
		std::cout << "tid:" << std::this_thread::get_id()
	        <<"begin!" << std::endl;
		//std::this_thread::sleep_for(std::chrono::seconds(2));
		uLong sum = 0;
		for (uLong i = begin_; i <= end_; i++)
		{
			sum += i;
		}
		std::cout << "tid:" << std::this_thread::get_id()
			<<"end!" << std::endl;
		return sum;
	}
private:
	int begin_;
	int end_;
};
int main()
{
	ThreadPool pool;
	pool.start(3);
	//问题2:如何设计这里的Result机制。使其和MyTask绑定
	Result res1=pool.submitTask(std::make_shared<MyTask>(1,100000000));
	Result res2 = pool.submitTask(std::make_shared<MyTask>(100000001, 200000000));
	Result res3 = pool.submitTask(std::make_shared<MyTask>(200000001, 300000000));

	//随着task被执行完,task对象没了,依赖于Task的Result对象也没了
	uLong sum1= res1.get().cast_<uLong>();  //get返回一个Any类型,怎么转换为具体的类型
	uLong sum2= res2.get().cast_<uLong>();
	uLong sum3= res3.get().cast_<uLong>();
	
	//Master -Slave线程模型
	//Master线程用来分解任务,然后给各个Salve线程分配任务
	//等待各个Slave线程执行完任务,返回结果
	//Master线程合并各个任务结果,输出
	std::cout << (sum1 + sum2 + sum3) << std::endl;
	/*pool.submitTask(std::make_shared<MyTask>());
	pool.submitTask(std::make_shared<MyTask>());
	pool.submitTask(std::make_shared<MyTask>());
	pool.submitTask(std::make_shared<MyTask>());*/

	getchar();
	//主线程结束后,分离线程给自动回收了,分离线程可能还没启动
	//std::this_thread::sleep_for(std::chrono::seconds(5));  //睡眠函数,主线程睡眠5s
}

4.7 初版线程池

4.8 初版线程池的问题

4.9解决死锁问题后的版本

cpp

#include "threadpool.h"
#include<functional>
#include<thread>
#include<iostream>

const int TASK_MAX_THRESHHOLD = 1024;
const int THREAD_MAX_THRESHHOLD = 10;
const int THREAD_MAX_IDLE_TIME = 10;  //单位:秒  线程最大空闲时间

//线程池构造
ThreadPool::ThreadPool()
	:initThreadSize_(4)
	, taskSize_(0)
	, idleThreadSize_(0)
	, taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
	, threadSizeThreshHold_(THREAD_MAX_THRESHHOLD)
	, curThreadSize_(0)
	, poolMode_(PoolMode::MODE_FIXED)
	,isPoolRunning_(false)
{}

//线程池析构
ThreadPool::~ThreadPool()
{
	isPoolRunning_ = false;
	

	//等待线程池里面所有的线程返回  有两种状态:阻塞 & 正在执行任务中
	std::unique_lock<std::mutex>lock(taskQueMtx_);
	notEmpty_.notify_all();
	exitCond_.wait(lock, [&]()->bool {return threads_.size() == 0; });
}

//设置线程池的工作模式
void ThreadPool::setMode(PoolMode mode)
{
	if (checkRunningState())
		return;
	poolMode_ = mode;
}

//设置task任务队列的上限阈值
void ThreadPool::setTaskQueMaxThreshHold(int threshhold)
{
	if (checkRunningState())
		return;
	taskQueMaxThreshHold_ = threshhold;
}

//设置线程池cached模式下线程阈值
void ThreadPool::setThreadSizeThreshHold(int threshhold)
{
	if (checkRunningState())
		return;
	if (poolMode_ == PoolMode::MODE_CACHED)
	{
		threadSizeThreshHold_ = threshhold;
	}
}

//给线程池提交任务  用户调用接口 传入任务 生产任务
Result ThreadPool::submitTask(std::shared_ptr<Task>sp)  //因为任务周期不知道,因此传一个智能指针
{
	//获取锁
	std::unique_lock<std::mutex>lock(taskQueMtx_);
	
	//线程的通信  等待任务队列有空余
	/*while (taskQue_.size() == taskQueMaxThreshHold_)  //与lamda表达式意义相同
	{
		notFull_.wait(lock);
	}*/
	//用户提交任务,最长不会阻塞1s,否则判断提交任务失败,返回
	if(!notFull_.wait_for(lock,std::chrono::seconds(1),
		[&]()->bool {return taskQue_.size() < (size_t)taskQueMaxThreshHold_; })) //lamda表达式
	{
		//表示notfull_等待1s,条件依然没有满足
		std::cerr << "task queue in full,submit task fail." << std::endl;
		return Result(sp,false);
	}
	//如果有空余,把任务放入任务队列中
	taskQue_.emplace(sp);
	taskSize_++;

	//因为新放了任务,任务队列肯定不空了,在notEmpty_上进行通知,赶快分配线程执行任务
	notEmpty_.notify_all();

	//需要根据任务数量和空闲线程的数量,判断是否需要创建新的线程出来  cached模式:小而快的线程
	if (poolMode_ == PoolMode::MODE_CACHED
		&& taskSize_ > idleThreadSize_
		&& curThreadSize_ < threadSizeThreshHold_)
	{
		std::cout << ">>>create new threat" << std::this_thread::get_id()<< std::endl;
		//创建新线程
		//创建thread线程对象的时候,把threadpool的线程函数给到thread对象   //绑定
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
		int threadId = ptr->getId();
		threads_.emplace(threadId, std::move(ptr));
		threads_[threadId]->start();
		curThreadSize_++;
		idleThreadSize_++;
		//threads_.emplace_back(ptr);  //unique_ptr不允许拷贝构造
		//threads_.emplace_back(std::move(ptr));
	}

	//返回任务的Result对象
	return Result(sp);
}
//开启线程池
void ThreadPool::start(int initThreadSize)
{
	//设置线程池的start
	isPoolRunning_ = true;

	//记录初始线程个数
	initThreadSize_ = initThreadSize;
	curThreadSize_ = initThreadSize;

	//创建线程对象
	for (int i = 0; i < (int)initThreadSize_; i++)
	{
		//创建thread线程对象的时候,把threadpool的线程函数给到thread对象   //绑定
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this,std::placeholders::_1));
		int threadId = ptr->getId();
		threads_.emplace(threadId, std::move(ptr));
		//threads_.emplace_back(ptr);  //unique_ptr不允许拷贝构造
		//threads_.emplace_back(std::move(ptr));
	}

	//启动所有线程
	for (int i = 0; i < (int)initThreadSize_; i++)
	{
		threads_[i]->start();
		idleThreadSize_++; //记录初始空闲线程的数量
	}
}

//定义线程函数  所有线程函数是从任务队列中消费任务
void ThreadPool::threadFunc(int threadid)
{
	    auto lastTime = std::chrono::high_resolution_clock().now();

		//所有任务必须执行完成,线程池才可以回收所有线程资源
	    //while (isPoolRunning_)
		for(;;)
	    {
			std::shared_ptr<Task> task;
			{
				//先获取锁
				std::unique_lock<std::mutex>lock(taskQueMtx_);

				std::cout << "tid:" << std::this_thread::get_id()
					<< "尝试获取任务" << std::endl;

				//cached模式下,有可能已经创建了很多的线程,但是空闲时间超过60s,应该把多余(超过初始的线程数量)的线程结束回收掉
				//当前时间-上一次线程执行的时间>60s

				//每一秒中返回一次  怎么区分:超时返回?还是有任务待执行返回
				while (taskQue_.size() == 0)
				{
					//线程池要结束,回收线程资源
					if (!isPoolRunning_)
					{
						threads_.erase(threadid);
						std::cout << "threadid:" << std::this_thread::get_id() << "exit" << std::endl;
						exitCond_.notify_all();
						return;
					}
					if (poolMode_ == PoolMode::MODE_CACHED)
					{
						//条件变量超时返回了
						if (std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1)))
						{
							auto now = std::chrono::high_resolution_clock().now();
							auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
							if (dur.count() >= THREAD_MAX_IDLE_TIME && curThreadSize_ > initThreadSize_)
							{
								//开始回收线程
								//记录线程数量的相关变量的值修改
								//把线程对象从线程列表容器中删除 
								//threadId=>thread对象=》删除
								threads_.erase(threadid);
								curThreadSize_--;
								idleThreadSize_--;
								std::cout << "threadid:" << std::this_thread::get_id() << "exit" << std::endl;
								return;
							}
						}
					}
					else
					{
						//等待notEmpty条件
						notEmpty_.wait(lock);
					}
					//
					/*if (!isPoolRunning_)
					{
						threads_.erase(threadid);
						std::cout << "threadid:" << std::this_thread::get_id() << "exit" << std::endl;
						exitCond_.notify_all();
						return;
					}*/
				}

				线程池要结束、回收线程资源
				//if (!isPoolRunning_)
				//{
				//	break;
				//}

				idleThreadSize_--;

				std::cout << "tid:" << std::this_thread::get_id()<< "获取任务成功" << std::endl;
				//从任务队列中取一个任务出来
				task = taskQue_.front();
				taskQue_.pop();
				taskSize_--;

				//如果依然有剩余任务,继续通知其他的线程执行任务
				if (taskQue_.size() > 0)
				{
					notEmpty_.notify_all();
				}
				//取出一个任务,进行通知,通知可以继续提交生产任务
				notFull_.notify_all();
			}//就应该把锁释放掉

				//当前线程负责执行这个任务
			if (task != nullptr)
			{
				//task->run();  //执行任务; 把任务的返回值setVal方法给到Result
				task->exec();
			}
			idleThreadSize_++;
			lastTime = std::chrono::high_resolution_clock().now();  //更新线程执行完任务的时间
		}
		/*threads_.erase(threadid);
		std::cout << "threadid:" << std::this_thread::get_id() << "exit" << std::endl;
		exitCond_.notify_all();*/
}

		
		

bool ThreadPool::checkRunningState() const
{
	return isPoolRunning_;
}
  线程方法实现
int Thread::generateId_ = 0;

//线程构造
Thread::Thread(ThreadFunc func)
	:func_(func)
	,threadId_(generateId_++)
{

}
//线程析构
Thread::~Thread()  
{

}

//启动线程
void Thread::start()
{
	//执行一个线程函数
	std::thread t(func_,threadId_);         //C++11的线程类  线程对象t 和线程函数func_
	t.detach();  //设置分离线程   将线程对象和线程函数进行分离,即在启动线程这个函数结束后,线程对象消失就消失了,不影响函数的运行
}
int Thread::getId()const
{
	return threadId_;
}

///Task方法实现
Task::Task()
	:result_(nullptr)
{

}
void Task::exec()
{
	if (result_ != nullptr)
	{
		result_->setVal(run());  //这里发生多态调用
	}
}
void Task::setResult(Result* res)
{
	result_ = res;
}
//  Result方法的实现
Result::Result(std::shared_ptr<Task> task, bool isValid)
	:isValid_(isValid)
	, task_(task)
{
	task->setResult(this);
}
Any Result::get()  //用户调用的
{
	if (!isValid_)
	{
		return"";
	}

	sem_.wait();  //task任务如果没有执行完,这里会阻塞用户的线程
	return std::move(any_);
}

void Result::setVal(Any any)  //谁调用的
{
	//存储task的返回值
	this->any_ = std::move(any);
	sem_.post();  //已经获取的任务的返回值,增加信号量资源
}

头文件

#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <vector>
#include<queue>
#include<memory>
#include<atomic>
#include<mutex>
#include<condition_variable>
#include<functional>
#include<unordered_map>


//Any类型:可以接收任意数据的类型
class Any
{
public:
	Any() = default;
	~Any() = default;
	Any(const Any&) = delete;
	Any& operator=(const Any&) = delete;
	Any(Any&&) = default;
	Any& operator=(Any&&) = default;
	
	//这个构造函数可以让Any类型接收任意其他的数据
	template<typename T>
	Any(T data) :base_(std::make_unique<Derive<T>>(data))
	{}

	//这个方法能把Any对象里面存储的data数据提取出来
	template<typename T>
	T cast_()
	{
		//我们怎么从base_找到它所指向的Derive对象,从它里面提取出data成员变量
		//基类指针=》派生类指针  RTTI
		Derive<T>* pd = dynamic_cast<Derive<T>*>(base_.get());
		if (pd == nullptr)
		{
			throw "type is unmatch!";
		}
		return pd->data_;
	}
private:
	//基类类型
	class Base
	{
	public:
		virtual ~Base()=default;
	};

	//派生类类型
	template<typename T>
	class Derive :public Base
	{
	public:
		Derive(T data) :data_(data)
		{}
		T data_;  //保存了任意的其他类型
	};

private:
	//定义一个基类指针
	std::unique_ptr<Base>base_;
};

//实现一个信号量类
class Semaphore
{
public:
	Semaphore(int limit = 0)
		:resLimit_(limit)
	{}
	~Semaphore() = default;
	
	//获取一个信号量资源
	void wait()
	{
		std::unique_lock<std::mutex>lock(mtx_);
		//等待信号量有资源,没有资源的话,会阻塞当前线程
		cond_.wait(lock, [&]()->bool {return resLimit_ > 0; });
		resLimit_--;
	}
	//增加一个信号量资源
	void post()
	{
		std::unique_lock<std::mutex>lock(mtx_);
		resLimit_++;
		cond_.notify_all();
	}
private:
	int resLimit_;
    std::mutex mtx_;
	std::condition_variable cond_;
};

//Task类型的前置声明
class Task;

//实现接收提交到的线程池的task任务执行完成后的返回值类型Resulit
class Result
{
public:
	Result(std::shared_ptr<Task> task, bool isValid = true);
	~Result() = default;

	//问题1 :setVal方法,获取任务执行完的返回值的
	void setVal(Any any);

	//问题2:get方法,用户调用这个方法获取task的返回值
	Any get();

private:
	Any any_;  //存储任务的返回值
	Semaphore sem_;  //线程通信信号量
	std::shared_ptr<Task>task_;  //指向对应获取返回值的任务对象
	std::atomic_bool isValid_;   //返回值是否有效
};
//任务抽象基类
class Task
{
public:
	Task();
	~Task() = default;
	void exec();
	void setResult(Result* res);
	//用户可以自定义任意任务类型,从Task继承,重写run方法,实现自定义任务处理
	virtual Any run() = 0;
private:
	Result* result_;  //Result对象的声明周期》》Task的
};
//线程池支持的模式
enum class PoolMode //enum class是为了解决枚举不同,但是枚举项相同的情况
{
	MODE_FIXED,  //固定数量的线程
	MODE_CACHED,  //线程数量可动态可增长
};
//线程类型
class Thread
{
public:
	
	//线程函数对象类型
	using ThreadFunc = std::function<void(int)>;

	//线程构造
	Thread(ThreadFunc func);
	//线程析构
	~Thread();
	//启动线程
	void start();
	//获取线程id
	int getId()const;

private:
	ThreadFunc func_;
	static int generateId_;
	int threadId_;  //保存线程ID
};

/*
example:
pool.start(4);

class Mytask:public Task
{
   public:
        void run(){//线程代码...}
};
    pool.submitTask(std::make_shared<Mytask>());
*/

//线程池类型
class ThreadPool
{
public:
	//线程池构造
	ThreadPool();
	//线程池析构
	~ThreadPool();
	
	//设置线程池的工作模式
	void setMode(PoolMode mode);

	//设置task任务队列的上限阈值
	void setTaskQueMaxThreshHold(int threshhold);

	//设置线程池cached模式下线程阈值
	void setThreadSizeThreshHold(int threshhold);

	//给线程池提交任务
	Result submitTask(std::shared_ptr<Task>sp);  //因为任务周期不知道,因此传一个智能指针

	//开启线程池
	void start(int initThreadSize=std::thread::hardware_concurrency());

	//禁止拷贝构造和拷贝赋值操作,用户使用线程池可以单独创建一个线程池对象,对线程池本身不能操作
	ThreadPool(const ThreadPool&) = delete;
	ThreadPool& operator=(const ThreadPool&) = delete;  
private:
	//定义线程函数
	void threadFunc(int threadid);

	//检查pool的运行状态
	bool checkRunningState() const;
private:
	//std::vector<std::unique_ptr<Thread>>threads_;//threads_  线程列表


	std::unordered_map<int, std::unique_ptr<Thread>>threads_;//threads_  线程列表
	size_t initThreadSize_;  //初始的线程数量
	std::atomic_int curThreadSize_;//记录当前线程池里面线程的总数量
	//记录空闲线程的数量
	std::atomic_int idleThreadSize_;
	int threadSizeThreshHold_;//cached模式下线程数量上限阈值

	std::queue<std::shared_ptr<Task>>taskQue_;  //任务队列
	std::atomic_int taskSize_;  //任务数量
	int taskQueMaxThreshHold_;  //任务队列数量上限阈值

	std::mutex taskQueMtx_;     //保证任务队列的线程安全  互斥锁
	std::condition_variable notFull_;
	std::condition_variable notEmpty_;  //表示任务队列不空
	std::condition_variable exitCond_;  //等到线程资源全部回收

	PoolMode poolMode_;  //当前线程池的工作模式

	//表示当前线程池的启动状态
	std::atomic_bool isPoolRunning_;

	

};

#endif

 测试文件:

#include<iostream>
#include<chrono>
#include<thread>
#include"threadpool.h"
//有些场景,是希望能够获取线程执行任务得到返回值的
//举例:
//1+。。。+3000的和
//thread1 1+..。+1000
//thread1 1000 + ... + 3000

using uLong = unsigned long long;
class MyTask :public Task
{
public:
	MyTask(uLong begin, uLong end)
		:begin_(begin)
		, end_(end)
	{}
	//问题1:怎么设计run函数的返回值,可以表示任意的类型
	Any run()  //run函数在线程池分配的线程中去做事情
	{
		std::cout << "tid:" << std::this_thread::get_id()
	        <<"begin!" << std::endl;
		//std::this_thread::sleep_for(std::chrono::seconds(3));
		uLong sum = 0;
		for (uLong i = begin_; i <= end_; i++)
		{
			sum += i;
		}
		std::cout << "tid:" << std::this_thread::get_id()
			<<"end!" << std::endl;
		return sum;
	}
private:
	uLong begin_;
	uLong end_;
};
int main()
{
	{
		ThreadPool pool;
		//开始启动线程池
		pool.start(4);
		Result res1 = pool.submitTask(std::make_shared<MyTask>(1, 100000000));
		uLong sum1 = res1.get().cast_<uLong>();
		std::cout << sum1 << std::endl;
	}
	std::cout << "main over!" << std::endl;
	getchar();
#if 0
	//问题:ThreadPool对象析构以后,怎麽样把线程池相关的线程资源全部回收
	{
		ThreadPool pool;
		//用户自己设置线程池的工作模式
		pool.setMode(PoolMode::MODE_CACHED);
		//开始启动线程池
		pool.start(4);
		//问题2:如何设计这里的Result机制。使其和MyTask绑定
		Result res1 = pool.submitTask(std::make_shared<MyTask>(1, 100000000));
		Result res2 = pool.submitTask(std::make_shared<MyTask>(100000001, 200000000));
		Result res3 = pool.submitTask(std::make_shared<MyTask>(200000001, 300000000));
		pool.submitTask(std::make_shared<MyTask>(200000001, 300000000));
		pool.submitTask(std::make_shared<MyTask>(200000001, 300000000));
		pool.submitTask(std::make_shared<MyTask>(200000001, 300000000));

		//随着task被执行完,task对象没了,依赖于Task的Result对象也没了
		uLong sum1 = res1.get().cast_<uLong>();  //get返回一个Any类型,怎么转换为具体的类型
		uLong sum2 = res2.get().cast_<uLong>();
		uLong sum3 = res3.get().cast_<uLong>();

		//Master -Slave线程模型
		//Master线程用来分解任务,然后给各个Salve线程分配任务
		//等待各个Slave线程执行完任务,返回结果
		//Master线程合并各个任务结果,输出
		std::cout << (sum1 + sum2 + sum3) << std::endl;
		/*pool.submitTask(std::make_shared<MyTask>());
		pool.submitTask(std::make_shared<MyTask>());
		pool.submitTask(std::make_shared<MyTask>());
		pool.submitTask(std::make_shared<MyTask>());*/

		getchar();
		//主线程结束后,分离线程给自动回收了,分离线程可能还没启动
		//std::this_thread::sleep_for(std::chrono::seconds(5));  //睡眠函数,主线程睡眠5s

	}
	#endif
	
}




//问题总结:不定时的运行出现死锁的情况,

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值