线程池代码

#include<iostream>
using namespace std;
#include<thread>
#include<mutex>
#include<queue>
#include<vector>
#include<functional>

class threadPool {

public:
	threadPool(int threadNum) :stop(false) {
		for (int i = 0; i < threadNum; i++) {
			threads.emplace_back([this]() {
					while(1){
						unique_lock<mutex> lock(mtx);
						condition.wait(lock, [this]() {
							return !tasks.empty() || stop;
							});
						if (stop&&tasks.empty()) {
							return;
						}
						function<void()>task(move(tasks.front()));
						tasks.pop();
						lock.unlock();
						task();
					}
				});//这里不使用push_back是因为push_back会进行拷贝构造,线程不支持拷贝
		}
	}

	~threadPool(){
		{
			unique_lock<mutex>lock(mtx);
			stop = true;
		}
		condition.notify_all();
		for (auto& t : threads) {
			t.join();
		}
	}
	
	template<class F,class... Args>
	void enqueue(F&& f, Args&&... args) {//函数模板,右值引用,可变参数
	//这里用户会传进来一个任务(函数),为了保障函数的一致性,先将函数转为task,所以需要将参数与函数绑定(bind),为了编制参数转换过程中不发生改变需要使用forward完美转换
		function<void()>task = bind(forward<F>(f), forward<Args> (args)...);//bind函数匹配,forward完美转发
		{
			unique_lock<mutex> lock(mtx);
			tasks.emplace(move(task));
		}
		condition.notify_one();
	}
private:
	vector<thread> threads;//创建一个容器来维护我们的线程
	queue<function<void()>> tasks;//用函数模版来创建一个任务队列

	mutex mtx;//互斥量
	condition_variable condition;//条件变量

	bool stop;//控制线程池什么时候停止
};
mutex mtx;
int main() {
	threadPool pool(4);
	
	for (int i = 0; i < 10; i++) {
		pool.enqueue([i] {
			{
				unique_lock<mutex>lock(mtx);
				cout << "task: " << i << " is running " << endl;
			}
			
			this_thread::sleep_for(chrono::seconds(1));

			{
				unique_lock<mutex>lock(mtx);
				cout << "task: " << i << " is done " << endl;
			}
			
			});
	}
	system("pause");
	return 0;
}

线程池代码知识点解析

lamda函数

  • 基本语法:[capture list] (parameter list) -> return type { function body }
    • 捕获列表(capture list)定义了Lambda表达式可以从封闭作用域中捕获哪些变量,以及是通过值(=)还是引用捕获(&)。
    • 参数列表(parameter list)与普通函数的参数列表类似,定义了Lambda表达式接受的参数。
    • 返回类型(return type)指定了Lambda表达式的返回类型。在C++14中,可以使用auto关键字来实现泛型返回类型。
    • 函数体(function body)包含了Lambda表达式的逻辑代码。

emplace_back

  • 这个与push_back作比较,使用这种方法插入数据相当于调用有参构造,而使用push_back调用相对于使用拷贝构造,线程本身是禁用拷贝与等号的,所以这里使用emplace_back,而且更高效一些

function函数模板

  • function是c++里面的一个模版类,用于存储、管理和调用任何类型的可调用对象,属于functional头文件
#include<functional>
//未定义的模板声明
template<class>
class function;//未定义

//定义模板
template<class R,class...Args>
class function<R(Args...)>;//定义从c++11开始
  • 其中R是返回类型,Args…是参数类型列表
  • 使用function函数模版可以接收普通函数,lamda表达式,函数对象,函数指针等
#include <iostream>
#include <functional>
#include <string>
// 普通函数
int multiply(int x, int y) {
    return x * y;
}
// 函数对象
struct Divider {
    int operator()(int x, int y) {
        if (y != 0) return x / y;
        return 0; // 避免除以0
    }
};
int main() {
    // 使用 std::function 定义一个可调用对象的容器
    std::function<int(int, int)> func;
    // 将普通函数存储到 std::function
    func = multiply;
    std::cout << "乘法结果: " << func(10, 5) << std::endl; // 应输出 50
    // 将 Lambda 表达式存储到 std::function
    func = [](int x, int y) { return x - y; };
    std::cout << "减法结果: " << func(10, 5) << std::endl; // 应输出 5
    // 将函数对象存储到 std::function
    func = Divider();
    std::cout << "除法结果: " << func(10, 5) << std::endl; // 应输出 2
    return 0;
}

右值引用

  • 右值指的是只能出现在等号右边的变量
  • 右值引用只能绑定右值(&&)
int value1=10;
int&& value2=value1;//错误
int&& value2=10;//正确
  • 但是可以通过move实现一个左值的右值引用
int value1=10;
int&& value2=move(value1);//正确
int&& value2=10;//正确

左值引用

  • 左值是指既可以出现在等号左边也可以出现在等号右边的变量
  • 左值引用只能绑定左值(&)
int value1=10;
int& value2=value1;//正确
int& value2=10;//错误
  • 但是const的左值引用可以
int value1=10;
int& value2=value1;//正确
const & value2=10;//正确

万能引用

  • 万能引用即可以接受左值也可以接收右值
  • 当一个变量或者参数被申明为T&&,并且需要推导T的类型时,该变量或者参数就被称为万能引用,当T被推导为左值那就是左值引用,T被推导为右值就是右值引用
  • 万能引用通常需要配合forward使用,可以确保参数以原来的形式被传递给其他函数
template<typename T>
void wrapper(T&&arg)
{
    //使用std::forward<T>将arg以原来的形式转发给其他函数
    otherFunction(std::forward<T>(arg));
}

注意:万能引用跟右值引用的形式很像,我们需要看是否运用到模板中,在模板中的才是万能引用

可变参数模版

// 展开函数
template <class T, class ...Args>
void print(T head, Args... rest)
{
   cout << " 打印:" << head << endl; // 打印当前参数
}

bind函数适配器

  • auto newCallable = std::bind(callable, arg_list);
    • 相当于是一个通用函数适配器,他接受一个可调用对象,生成一个新的可调用对象来适配原来的参数列表
    • 第一个参数为要绑定的可调用对象
    • 第二个参数为参数列表
  • 在绑定类成员函数的时候,传递的参数有变化
    • 第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址

forward完美转发

  • 主要用于在函数模板中高效的传递参数,确保参数的值类别在转发过程中保持不变
  • 他的实现依赖于类型推导和move函数,但是forward只在特定条件下将类型转化为右值
template<typename T>
void forward_example(T&& arg) {
process(std::forward<T>(arg)); // 保留参数的值类别
}
  • 尖括号里面传需要推导的变量类型,小括号里面传参数

move函数

  • 用于将一个对象的状态或者所有权转移给另外一个对象而不发生复制,比复制资源更加高效
#include <iostream>
#include <vector>

class MovableClass {
public:
MovableClass() { std::cout << "Constructor called\n"; }
MovableClass(const MovableClass&) { std::cout << "Copy constructor called\n"; }
MovableClass(MovableClass&&) { std::cout << "Move constructor called\n"; }
};

int main() {
std::vector<MovableClass> vec;
MovableClass obj;
vec.push_back(obj); // 这将调用复制构造函数
vec.push_back(std::move(obj)); // 这将调用移动构造函数
}
  • 在上面的代码中,第一次push_back调用复制构造函数,因为obj是一个左值。第二次push_back使用std::move,它将obj转换为右值引用,从而调用移动构造函数。

时间库chrono

-C++11 引入的时间处理库,它提供了 时间点 (Time Points)、持续时间 (Durations) 和 时钟 (Clocks) 的概念。

#include <iostream>
#include <chrono>
#include <thread>

void someFunction() {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
}

int main() {
auto start = std::chrono::high_resolution_clock::now(); // 开始计时
someFunction();
auto end = std::chrono::high_resolution_clock::now(); // 结束计时

auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Function took " << duration.count() << " milliseconds to execute." << std::endl;

return 0;
}
  • auto now = std::chrono::system_clock::now(); // 获取当前系统时间
    • std::chrono::system_clock: 系统时钟,与系统时间同步。
    • std::chrono::steady_clock: 单调时钟,不受系统时间调整影响。 (首选)
    • std::chrono::high_resolution_clock: 提供最高精度的时钟。

更多推荐