c++实现线程池(真实入门!)

本文介绍如何使用C++和Windows.h构建简单线程池,包括任务封装、线程池框架构建及成员函数实现。适用于初学者,涵盖线程重复利用、自定义任务函数、线程数调节等功能。

10分钟学会用C++编写一个简单的线程池(使用windows.h头文件)

前言

这是我们操作系统专业课的一次编程作业,作为菜鸡的我搜索了大量的线程池博客,但不是代 码太秀,比较难懂,就是实现的线程池功能太过强大,不适合入门,或是讲解重点并没解决我 的问题,而不同博客使用的头文件又不同;很难有只需一篇就能让菜鸡搞懂的博客。

所以我打算以我从零开始理解的角度完成这篇 实验报告 博客,能让最菜的菜鸡也能看懂并手 动实现一个最简单的线程池。

但要做到这一点,需要有一点点前提条件

  1. 只需要有简单的c++面向对象编程的能力。
  2. 会使用win自带的 “windows.h” 头文件进行多线程编程。
  3. 对线程有基本的理解。

而我们最终能得到的线程池拥有的功能有

  1. 实现线程池最基本的功能能:线程重复利用,少量线程完成大量工作(线程数<任务 数)。
  2. 可自己设计任务函数,而不是简单的模拟。(同GreatThread() 传入参数的方法类似)
  3. 可调节线程池的线程数,空闲线程等待时间等。
  4. 可随时添加新任务,添加结束后可调用等待函数等待线程完成所有工作。
  5. 不能直接接收返回值,但返回值可利用传入的参数指针传回。

基本概念

  1. 为啥要有线程池
    一般的多线程编程,往往是一个线程对应一个 ‘任务’ 。但由于线程的建立于销毁也会造成一部分开销,所以当任务过多的时候,若还是一个任务对应一个线程,线程的反复建立与销毁,会造成大量的的资源浪费。

  2. 什么是任务
    这一点是大部分博客都没有提及的,以我的理解,这里的任务所指的是你打算在线程函数里面完成的工作,比如打印 “hello word” 或者做一些计算之类的。
    而大部分任务往往都能封装进一个函数,所以这里的任务往往就是指一个有明确目的函数(相同函数传入不同参数也能看成不同的任务)

  3. 什么是线程池
    如果我们重复利用几个线程,去完成比线程数更多的任务,那不就避免了线程建立和销毁造成的开销么。
    一句话说线程池就是一种利用少量线程解决大量任务(线程数 < 任务数)的工具。
    而高级的线程池会从线程的数量,线程的活跃时间,任务的存取策略等角度进行优化,以达到更高效目的。

  4. 如何实现
    我们可以把所有的任务放在一个队列内,并创建一定数量的线程。线程空闲时,可以自动从任务队列中取出任务,任务完成后,又进入空闲,并尝试取出下一个任务。而这,就是最简单的线程池了。

代码实现

“任务”的封装

综上所述,我们需要实现的任务需要有这样的特性:

  1. 创建任务和运行任务可以不同步(不同于简单的调用函数)。
  2. 因为要放入队列,所以需要有具体的数据结构。
  3. 需要固定任务的格式(总不能让用户随便传入啥类型的函数都行吧,得规范一下)

解决方案

首先,统一任务函数的格式:”参数为 void 指针,返回值为void(没有返回值)“。

这样的话,利用void指针可以指向任何数据的特性,任务函数可以传入任意类型的参数了,也可以利用指针传回返回值。

// 声明fp_return_void类型
typedef void(*fp_return_void)(void *paramter);

见:函数指针的用法

其次,将函数指针和参数指针分开来, 并进行封装,想运行函数的时候,直接调用函数指针并把参数传入即可(见run()成员函数)

class Task
{
   
   
private:
	// 函数指针和参数指针
	fp_return_void fp;
	void *paramter;

	// 构造函数,需传入函数指针和参数指针
	Task(fp_return_void fp, void *param) :
		fp(fp), paramter(param) {
   
   }

	// 运行任务
	void run()
	{
   
   
		fp(paramter);
	}

public:// 声明为线程池的友元类
	friend class KyThreadPool;

};

Task使用方法:

// 声明自己的任务函数
void FuncName(void *lpParamter)
{
   
   
	int *pmd = (int *)lpParamter;// 假设需要传入int指针,(int *)表示强制类型转换,相当于告诉函数void指针指向的是int类型。
	cout << "task " << pmd[0] << " is working!\n";
}

//声明参数
int x = 1;
int *p = &x;

// 声明一个任务
Task onetask(FuncName, p); 
onetask.run(); // 运行任务

线程池框架构建

根据线程池的概念,线程池的主体必要两个东西:

  1. 线程函数,有任务的时候做任务,没任务的时候等任务,等久了就自动结束。
  2. 任务队列,用于存放任务,供线程函数提取任务用。(见:STL队列用法

还需要一些池子的参数:

  1. 核心线程数/最大线程数(为了简便,假设核心线程数 = 最大线程数)
  2. 空闲线程等待时间,空闲线程也会占用cpu,所以不能让空闲线程活太久
  3. 现存线程数量,活跃线程数量,线程结束标志等,用于管理线程

以及几个供给用户使用的函数:

  1. 等待线程池函数,线程停止接收任务,等待剩余线程完成任务。
  2. 重启线程池函数,使线程池重新工作
  3. 添加任务函数,用于增加一个任务到队列中。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值