C特性版高效线程池知识速递一、this指针this是C中非静态成员函数里的一个隐藏函数它指向调用当前成员函数的那个对象本身谁调用函数this就指向谁this是当前对象的地址*this是当前对象本身静态成员函数没有this指针用法1.区分成员变量和函数形参class Person { private: int age; // 成员变量 public: // 形参名和成员变量同名 void setAge(int age) { // this-age 表示对象的成员变量 // age 表示函数的形参 this-age age; } };2.链式调用返回*this让成员函数返回当前对象的引用实现连续调用class Person { private: int age; public: Person setAge(int age) { this-age age; return *this; // 返回当前对象本身 } Person showAge() { cout 年龄 this-age endl; return *this; } }; int main() { Person p; // 链式调用 p.setAge(25).showAge(); return 0; }3.在成员函数中获取对象地址void printAddress() { cout 当前对象地址 this endl; }4.委托构造class Student { public: string name; int age; // 主构造 Student(string n, int a) : name(n), age(a) {} // 委托构造复用上面的构造函数 Student(string n) : this(n, 18) {} };5.模板类中显式指明依赖类作用域模板派生类继承模板基类时编译器找不到基类成员要用this-显式告诉编译器是类成员templatetypename T class Base { protected: int val 100; }; templatetypename T class Derive : public BaseT { public: void show() { // 必须加 this-否则编译报错 cout this-val endl; } };this指向Derive,如果是自己的成员val,继承的基类也有val,那this-val如何区分是哪个类的val同名成员隐藏规则this-val默认优先找当前类自己的成员隐藏掉基类的同名成员// 加类名作用域强制访问【基类】的val cout BaseT::val endl;6.const this普通this指针指针本身不可改指向可以通过this修改对象成员const this指针不能通过this修改对象任何成员class Person { int age; public: void show() const { age 20; // 报错const this 禁止修改成员 } };mutable突破const this限制用mutable修饰的成员哪怕在const函数里也能改mutable不受const this约束this在多线程下是否安全多个线程访问同一个对象(有读有写)会产生数据竞争class Test { int num 0; public: void add() { this-num; // 多个线程同时执行这句 灾难 } };怎么让this指向的对象变成线程安全给共享成员变量加锁#include mutex class Test { int num 0; mutex mtx; // 锁 public: void add() { lock_guardmutex lock(mtx); // 加锁 this-num; } };二、std::function作用把一切可调用对象(函数lambda仿函数绑定器)统一包装成同一种类型方便存储传递回调是万能函数包装器可以理解成一个函数指针语法// 格式 std::function返回值(参数列表) 变量名; // 例子包装一个返回int、参数是int,int的可调用对象 std::functionint(int, int) func;示例1.包装普通函数#include iostream #include functional using namespace std; int add(int a, int b) { return a b; } int main() { functionint(int, int) f add; cout f(2, 3) endl; // 输出 5 }2.包装lambdafunctionint(int, int) f [](int a, int b) { return a * b; }; cout f(2, 3); // 63.配合bind包装成员函数Test t; functionint(int) f bind(Test::show, t, placeholders::_1); cout f(5); // 50用途1.实现回调函数void run(functionvoid() callback) {//参数是可调用对象 callback(); // 调用回调 } int main() { run([]() { cout 回调执行\n; }); }2.把不同可调用对象存入同一容器vectorfunctionint(int, int) vec; vec.push_back(add); vec.push_back([](int a,int b){return a-b;});3.实现状态机、策略模式、事件分发状态机#include iostream #include functional using namespace std; // 状态枚举 enum State { IDLE, RUN, STOP }; class StateMachine { public: // 当前状态执行函数 functionvoid() curState; // 切换状态 void switchState(State s) { switch (s) { case IDLE: curState [this](){ idle(); }; break; case RUN: curState [this](){ run(); }; break; case STOP: curState [this](){ stop(); }; break; } } // 每一帧执行当前状态 void update() { if(curState) curState(); } private: void idle() { cout 空闲状态\n; } void run() { cout 运行状态\n; } void stop() { cout 停止状态\n; } }; int main() { StateMachine sm; sm.switchState(IDLE); sm.update(); sm.switchState(RUN); sm.update(); sm.switchState(STOP); sm.update(); return 0; }事件分发#include iostream #include functional #include vector #include unordered_map using namespace std; // 事件分发器 class EventDispatcher { public: // 事件回调携带int参数 using EventCb functionvoid(int); unordered_mapint, vectorEventCb eventMap; // 绑定事件 void on(int eventId, EventCb cb) { eventMap[eventId].push_back(cb); } // 触发事件 void emit(int eventId, int msg) { if(!eventMap.count(eventId)) return; for(auto cb : eventMap[eventId]) { cb(msg); } } }; int main() { EventDispatcher ed; // 绑定事件1001 ed.on(1001, [](int val){ cout 模块A收到 val endl; }); ed.on(1001, [](int val){ cout 模块B收到 val*2 endl; }); // 触发事件 ed.emit(1001, 666); return 0; }特性1.空检查functionvoid() f; if(!f){}//判断是否为空2.可以像普通函数一样调用f();3.与函数指针的区别对比维度函数指针std::function可装普通全局 / 静态函数✅✅可装 Lambda带捕获❌✅可装仿函数 /std::bind❌✅可装类非静态成员函数❌✅统一类型存容器❌✅保存调用状态❌✅安全判空❌ 易崩溃✅ 可判断运行开销极低略高可忽略C 版本原生就有C11 起手写简易function1.抽象基类(提供统一接口)templatetypename Ret,typename... Args//...表示一包参数返回值Ret这个基类能适配任意返回值任意参数的函数 struct FuncBase{ virtual ~FuncBase()default;//虚析构函数先调子类析构再调基类析构不加的话delete基类指针只会调用基类析构子类内存泄漏编译器自动生成默认实现 virtual Ret invoke(Args,,, args)0;//0表示这个函数没有实现强制子类必须实现它 };2.模板派生类// 包装具体可调用对象 templatetypename Ret, typename... Args, typename F//F真正的可调用对象 struct FuncImpl : FuncBaseRet, Args... { F func; // 存 lambda/函数/仿函数等可调用对象的地方 FuncImpl(F f) : func(std::move(f)) {}//std::move避免拷贝 // 虚函数转发调用 Ret invoke(Args... args) override {//override显式声明是重写虚函数 return func(std::forwardArgs(args)...);//std::forward完美转发保持参数的左值右值属性不拷贝、不修改类型原汁原味把参数传给内部参数 } }; 3.实现自己的functiontemplatetypename Ret, typename... Args class MyFunction { private: // 持有抽象基类指针隐藏真实类型 FuncBaseRet, Args...* ptr nullptr; public: // 接收任意可调用对象 templatetypename F MyFunction(F f) {//万能指针能接受左值右值lambda函数指针 // 派生类装真实对象基类指针指向它 ptr new FuncImplRet, Args..., F(std::forwardF(f)); } // 重载 () 对外统一调用 Ret operator()(Args... args) { return ptr-invoke(std::forwardArgs(args)...); } ~MyFunction() { delete ptr; } };三、std::move作用单纯做类型强转把任意一个变量强制转为右值引用不移动数据只是告诉编译器可以偷资源真正移动数据的是移动构造函数template typename T typename remove_referenceT::type move(T t) { return static_casttypename remove_referenceT::type(t); }//就是一个强制类型转换右值引用专门用来绑定临时对象实现移动语义右值引用和普通变量的区别int a10的10编译器会在栈上开辟一块独立内存存10,变量a持有这块内存生命周期由a决定inta10;的10是临时右值不会额外开新内存存10,用完直接销毁用法1.移动对象避免拷贝string s1 hello world; string s2 s1; // 拷贝慢 string s3 move(s1); // 移动快这也是为什么发明右值引用的原因老式C痛点完整拷贝内存数据复制一遍效率低右值引用把s1的内存资源直接转移给s3s1变空另拷贝极速完成编译器看到右值优先调用移动构造函数移动构造函数内部做指针交换四、std::forward作用完美转发把参数的属性(左值右值)完封不动转发给下一个函数为什么需要std::forwardtemplatetypename T void func(T t) { other_func(t); // 这里 t 永远变成了 左值 }即使你传入右值进入函数后t有名字了 变成左值导致右值属性丢失无法触发移动语义五、std::future为什么需要std::futrue类?std::futrue是用来获取异步线程的返回值和异常的是主线程和子线程之间的结果传递通道一个子线程干完活想把结果告诉主线程怎么传需要用到std::futruestd::future就是异步任务的结果提货单拿着单子等线程跑完再取返回值特性1..get()只能调用一次一个future只能获取一次结果取完失效get()会阻塞当前线程直到异步任务完成2.bool vaild()判断当前future是否还持有结果3.void wait()只等待完成不获取结果4.wait_for(时间段)超时等待不卡死线程5.share()转为shared_future,允许多个线程同时获取结果调用多次.get()std::async:本质模板函数发起异步任务返回一个futurefuture对象.get()取任务返回值等于高级版thread自动管理不用join// async 是函数调用它 auto ft std::async(func); // ft 是 future 类实例.get() 是它的成员函数 int res ft.get();std::packaged_task:是一个模板类包装可调用对象的包装器把函数和future绑定在一起包装一函数执行任务后结果自动放进future,可以手动控制什么时候执行任务核心三步骤创建packaged_task包装你的函数从它身上取出future,调用task()执行任务future.get()拿结果#include iostream #include future using namespace std; int add(int a, int b) { return a b; } int main() { // 1. 创建 packaged_task包装函数 packaged_taskint(int, int) task(add); // 2. 取出 future futureint fu task.get_future(); // 3. 执行任务手动调用 task(10, 20); // 4. 拿结果 cout fu.get() endl; // 30 } async和packaged_task最大的区别async是自动执行packaged_task必须手动调动task()才可以std::promisestd::future:std::promiseT生产者主动存值、发数据std::futureT消费者等待取值、拿数据#include iostream #include thread #include future using namespace std; void setData(promiseint pro) { // 延迟模拟耗时操作 this_thread::sleep_for(chrono::seconds(1)); pro.set_value(666); // 存入数据 } int main() { promiseint p; futureint f p.get_future(); // 绑定成对 thread t(setData, move(p)); // 移动语义传promise cout 等待子线程赋值...\n; int res f.get(); // 阻塞等待拿到值才继续 cout 收到值 res endl;//res666 t.join(); return 0; } 二者成对绑定跨线程传值 / 传结果组件角色使用场景执行方式promisefuture主动发值 等待收值线程间手动传递数据手动 set_value 发packaged_taskfuture包装函数 获取函数返回值线程池封装任务调用任务自动回填结果asyncfuture一键异步执行简单异步任务自动开线程执行六、tuple定义作用就是打包一组数据元组能装任意多个、任意不同类型的数据容器长度固定混放不同类型的元素创建1.直接构造tupleint string,double t(1,张三,3.14);2.make_tuple自动推导(数据类型)auto t2make_tuple(20,李四,99.9);3.花括号初始化tupleint,intt3{5,6};特性1.取值get下标coutget0 (t);//下标从0开始2.批量赋值tieint a;string s;double d; tie(a,s,d)t;3.结构化绑定auto [num,str,val]make_tuple(10,abc,5.5);//变量对象存入4.tuple_sizedecltype(t)::value;获取元素个数七、std::apply作用把元组tuple的所有元素依次解包传入函数调用std::apply(函数元组)void add(int a,int b){ coutabendl; } int main(){ auto tmake_tuple(3,5); apply(add,t); return 0; }//搭配lambda auto tmake_tuple(10,20,30); apply([](int x,int y,int z){coutxyz;},t);八、lambda格式[捕获列表]参数列表mutable-返回值类型{函数体}[]捕获抓外部变量[]不捕获[]值捕获[]引用捕获[a]只捕获a值[a]只捕获a引用mutable值捕获也能修改副本-T :指定返回值可省略自动推导特性1.值捕获默认不能改加mutable可改脚本不改原变量用的是拷贝过来的旧值int x5; auto f[]()mutable{x100;}; f();//外面x还是52.lambda用外部变量有三类用法值捕获、引用捕获、传参九、万能引用定义模板中T万能引用int-纯右值引用万能引用既能接受左值也能接受右值完美转发的基础推导规则传左值T推导成T左值引用传右值T推导成T右值引用引用折叠 - - 只要有一个左值引用结果一定是左值引用遇左则左全右才右传入字面量T代入得T没有引用叠加直接右值引用十、智能指针作用自动管理堆内存无需手动delete底层依靠引用计数/独占所有权自动析构三大智能指针std::unique_ptr独占智能指针独占所有权同一个资源只能一个指针持有不能拷贝只能移动开销最小auto 变量make_unique类型(初始值);//模板函数make_unique干了两件事在堆上new构造对象打包返回unique_ptr unique_ptrint p1make_uniqueint (10); unique_ptrint p2move(p1);std::shared_ptr共享智能指针引用计数机制多人共享同一块内存计数0 自动释放内存可拷贝可赋值shared_ptrint p1(new int(10));//不推荐直接new构造异常不安全auto pmake_sharedint(666);//这里make_shared干了两件事在堆上new构造对象自动创建引用计数控制块打包返回shared_ptr一次性连续开辟两块内存对象控制块 //make_shared优点只分配一次内存异常安全不会内存泄漏 shared_ptrint p2p;//计数1 coutp.use_count();//查看引用计数std::weak_ptr弱指针这里不做详细讲解线程池代码#pragma once #include condition_variable #include functional #include future #include iostream #include mutex #include queue #include thread #include vector class pool { private: int thread_count; std::queuestd::functionvoid() works; std::vectorstd::thread thd; std::mutex mtx; std::condition_variable cv; bool runflag true; public: pool(int n 4) : thread_count(n) { for (int i 0; i thread_count; i) { thd.emplace_back([this]() { while (true) { std::functionvoid() task; { std::unique_lockstd::mutex lock(mtx); cv.wait(lock, [this] { return !works.empty() || !runflag; }); if (!runflag works.empty()) return; if (!works.empty()) { task std::move(works.front()); works.pop(); } } if (task) task(); } }); } } ~pool() { { std::unique_lockstd::mutex lock(mtx); runflag false; } cv.notify_all(); for (auto t : thd) { if (t.joinable()) t.join(); } } public: template typename F, typename... Args auto enqueue(F f, Args... args) - std::futuretypename std::invoke_resultF, Args...::type { using return_type typename std::invoke_resultF, Args...::type; auto task std::make_shared std::packaged_taskreturn_type()([func std::forwardF(f), args std::make_tuple( std::forwardArgs( args)...)]() mutable { return std::apply( func, args); }); std::futurereturn_type res task-get_future(); { std::unique_lockstd::mutex lock(mtx); works.emplace([task]() { (*task)(); }); } cv.notify_one(); return res; } };核心成员变量int thread_count; // 线程数量 std::queuestd::functionvoid() works; // 任务队列存“待执行函数” std::vectorstd::thread thd; // 线程池本体 std::mutex mtx; // 保护队列 std::condition_variable cv; // 线程等待/唤醒 bool runflag true; // 线程池运行标记任务队列用functionvoid():不管你丢什么任务全部包装成无参无返回的函数同一存储构造函数pool(int n 4) : thread_count(n) { for (int i 0; i thread_count; i) { thd.emplace_back([this]() { // 线程lambda while (true) { std::functionvoid() task; { std::unique_lockstd::mutex lock(mtx); // 等待有任务 或 线程池停止 cv.wait(lock, [this] { return !works.empty() || !runflag; }); // 退出条件停止无任务 if (!runflag works.empty()) return; // 取任务 task std::move(works.front()); works.pop(); } // 解锁后执行任务非常重要 if (task) task(); } }); } }[this]()捕获this:线程内部能访问县城池的队列、条件变量构造函数让线程具备等待 - 取任务 - 执行的能力没有任务-睡觉有任务/要退出-醒过来enqueue提交任务template typename F, typename... Args//接受任意类型的函数可调用函数任意数量参数 auto enqueue(F f, Args... args)//万能引用可接收左值和右值 - std::futuretypename std::invoke_resultF, Args...::type {//std::invoke推导f的返回值类型,future是未来取结果的容器 // 这一步相当于定义了一个 “万能快递箱”能装下任何类型的 “包裹函数”和 “配件参数”还承诺会返回一个 “取件码future” using return_type typename std::invoke_resultF, Args...::type; // 使用 packaged_task 封装任务并绑定返回值 auto task std::make_shared std::packaged_taskreturn_type()([func std::forwardF(f), args std::make_tuple( std::forwardArgs( args)...)]() mutable { return std::apply( func, args); // 把传入的可变参数比如 // 1,2打包成一个std::tuple元组把元组args拆成单个参数传给func执行 }); // 获取 future 对象获取 future给调用者返回 “取件码” std::futurereturn_type res task-get_future(); { std::unique_lockstd::mutex lock(mtx); works.emplace([task]() { (*task)(); }); // 将任务包装为 void() 类型 } cv.notify_one(); return res; }作用接受任意函数任意参数-包装成任务-扔进任务队列-返回future让你拿结果invoke_resultF,Args...::type:自动推导函数返回值类型超级强大函数声明auto enqueue(F f,Args... args)-std::futuretypename std::invoke_resultF,Args...::type这个函数主要干了什么invoke_result推导返回值类型、packaged_task封装任务、获取future、emplace把任务扔给队列、唤醒一个线程来干活、返回future实现了提交任意函数-自动包装-线程执行-返回结果一句话串完线程池流程外部传入函数可变参数——万能引用接收——完美转发打包tuple——lambda捕获保存——封装packaged_task生成future——任务入队——工作线程cv唤醒——move取出任务——apply解包执行——主线程future.get获取结果
C++ 进阶核心特性总结:手写工业级高效通用线程池(超全原理精讲)
C特性版高效线程池知识速递一、this指针this是C中非静态成员函数里的一个隐藏函数它指向调用当前成员函数的那个对象本身谁调用函数this就指向谁this是当前对象的地址*this是当前对象本身静态成员函数没有this指针用法1.区分成员变量和函数形参class Person { private: int age; // 成员变量 public: // 形参名和成员变量同名 void setAge(int age) { // this-age 表示对象的成员变量 // age 表示函数的形参 this-age age; } };2.链式调用返回*this让成员函数返回当前对象的引用实现连续调用class Person { private: int age; public: Person setAge(int age) { this-age age; return *this; // 返回当前对象本身 } Person showAge() { cout 年龄 this-age endl; return *this; } }; int main() { Person p; // 链式调用 p.setAge(25).showAge(); return 0; }3.在成员函数中获取对象地址void printAddress() { cout 当前对象地址 this endl; }4.委托构造class Student { public: string name; int age; // 主构造 Student(string n, int a) : name(n), age(a) {} // 委托构造复用上面的构造函数 Student(string n) : this(n, 18) {} };5.模板类中显式指明依赖类作用域模板派生类继承模板基类时编译器找不到基类成员要用this-显式告诉编译器是类成员templatetypename T class Base { protected: int val 100; }; templatetypename T class Derive : public BaseT { public: void show() { // 必须加 this-否则编译报错 cout this-val endl; } };this指向Derive,如果是自己的成员val,继承的基类也有val,那this-val如何区分是哪个类的val同名成员隐藏规则this-val默认优先找当前类自己的成员隐藏掉基类的同名成员// 加类名作用域强制访问【基类】的val cout BaseT::val endl;6.const this普通this指针指针本身不可改指向可以通过this修改对象成员const this指针不能通过this修改对象任何成员class Person { int age; public: void show() const { age 20; // 报错const this 禁止修改成员 } };mutable突破const this限制用mutable修饰的成员哪怕在const函数里也能改mutable不受const this约束this在多线程下是否安全多个线程访问同一个对象(有读有写)会产生数据竞争class Test { int num 0; public: void add() { this-num; // 多个线程同时执行这句 灾难 } };怎么让this指向的对象变成线程安全给共享成员变量加锁#include mutex class Test { int num 0; mutex mtx; // 锁 public: void add() { lock_guardmutex lock(mtx); // 加锁 this-num; } };二、std::function作用把一切可调用对象(函数lambda仿函数绑定器)统一包装成同一种类型方便存储传递回调是万能函数包装器可以理解成一个函数指针语法// 格式 std::function返回值(参数列表) 变量名; // 例子包装一个返回int、参数是int,int的可调用对象 std::functionint(int, int) func;示例1.包装普通函数#include iostream #include functional using namespace std; int add(int a, int b) { return a b; } int main() { functionint(int, int) f add; cout f(2, 3) endl; // 输出 5 }2.包装lambdafunctionint(int, int) f [](int a, int b) { return a * b; }; cout f(2, 3); // 63.配合bind包装成员函数Test t; functionint(int) f bind(Test::show, t, placeholders::_1); cout f(5); // 50用途1.实现回调函数void run(functionvoid() callback) {//参数是可调用对象 callback(); // 调用回调 } int main() { run([]() { cout 回调执行\n; }); }2.把不同可调用对象存入同一容器vectorfunctionint(int, int) vec; vec.push_back(add); vec.push_back([](int a,int b){return a-b;});3.实现状态机、策略模式、事件分发状态机#include iostream #include functional using namespace std; // 状态枚举 enum State { IDLE, RUN, STOP }; class StateMachine { public: // 当前状态执行函数 functionvoid() curState; // 切换状态 void switchState(State s) { switch (s) { case IDLE: curState [this](){ idle(); }; break; case RUN: curState [this](){ run(); }; break; case STOP: curState [this](){ stop(); }; break; } } // 每一帧执行当前状态 void update() { if(curState) curState(); } private: void idle() { cout 空闲状态\n; } void run() { cout 运行状态\n; } void stop() { cout 停止状态\n; } }; int main() { StateMachine sm; sm.switchState(IDLE); sm.update(); sm.switchState(RUN); sm.update(); sm.switchState(STOP); sm.update(); return 0; }事件分发#include iostream #include functional #include vector #include unordered_map using namespace std; // 事件分发器 class EventDispatcher { public: // 事件回调携带int参数 using EventCb functionvoid(int); unordered_mapint, vectorEventCb eventMap; // 绑定事件 void on(int eventId, EventCb cb) { eventMap[eventId].push_back(cb); } // 触发事件 void emit(int eventId, int msg) { if(!eventMap.count(eventId)) return; for(auto cb : eventMap[eventId]) { cb(msg); } } }; int main() { EventDispatcher ed; // 绑定事件1001 ed.on(1001, [](int val){ cout 模块A收到 val endl; }); ed.on(1001, [](int val){ cout 模块B收到 val*2 endl; }); // 触发事件 ed.emit(1001, 666); return 0; }特性1.空检查functionvoid() f; if(!f){}//判断是否为空2.可以像普通函数一样调用f();3.与函数指针的区别对比维度函数指针std::function可装普通全局 / 静态函数✅✅可装 Lambda带捕获❌✅可装仿函数 /std::bind❌✅可装类非静态成员函数❌✅统一类型存容器❌✅保存调用状态❌✅安全判空❌ 易崩溃✅ 可判断运行开销极低略高可忽略C 版本原生就有C11 起手写简易function1.抽象基类(提供统一接口)templatetypename Ret,typename... Args//...表示一包参数返回值Ret这个基类能适配任意返回值任意参数的函数 struct FuncBase{ virtual ~FuncBase()default;//虚析构函数先调子类析构再调基类析构不加的话delete基类指针只会调用基类析构子类内存泄漏编译器自动生成默认实现 virtual Ret invoke(Args,,, args)0;//0表示这个函数没有实现强制子类必须实现它 };2.模板派生类// 包装具体可调用对象 templatetypename Ret, typename... Args, typename F//F真正的可调用对象 struct FuncImpl : FuncBaseRet, Args... { F func; // 存 lambda/函数/仿函数等可调用对象的地方 FuncImpl(F f) : func(std::move(f)) {}//std::move避免拷贝 // 虚函数转发调用 Ret invoke(Args... args) override {//override显式声明是重写虚函数 return func(std::forwardArgs(args)...);//std::forward完美转发保持参数的左值右值属性不拷贝、不修改类型原汁原味把参数传给内部参数 } }; 3.实现自己的functiontemplatetypename Ret, typename... Args class MyFunction { private: // 持有抽象基类指针隐藏真实类型 FuncBaseRet, Args...* ptr nullptr; public: // 接收任意可调用对象 templatetypename F MyFunction(F f) {//万能指针能接受左值右值lambda函数指针 // 派生类装真实对象基类指针指向它 ptr new FuncImplRet, Args..., F(std::forwardF(f)); } // 重载 () 对外统一调用 Ret operator()(Args... args) { return ptr-invoke(std::forwardArgs(args)...); } ~MyFunction() { delete ptr; } };三、std::move作用单纯做类型强转把任意一个变量强制转为右值引用不移动数据只是告诉编译器可以偷资源真正移动数据的是移动构造函数template typename T typename remove_referenceT::type move(T t) { return static_casttypename remove_referenceT::type(t); }//就是一个强制类型转换右值引用专门用来绑定临时对象实现移动语义右值引用和普通变量的区别int a10的10编译器会在栈上开辟一块独立内存存10,变量a持有这块内存生命周期由a决定inta10;的10是临时右值不会额外开新内存存10,用完直接销毁用法1.移动对象避免拷贝string s1 hello world; string s2 s1; // 拷贝慢 string s3 move(s1); // 移动快这也是为什么发明右值引用的原因老式C痛点完整拷贝内存数据复制一遍效率低右值引用把s1的内存资源直接转移给s3s1变空另拷贝极速完成编译器看到右值优先调用移动构造函数移动构造函数内部做指针交换四、std::forward作用完美转发把参数的属性(左值右值)完封不动转发给下一个函数为什么需要std::forwardtemplatetypename T void func(T t) { other_func(t); // 这里 t 永远变成了 左值 }即使你传入右值进入函数后t有名字了 变成左值导致右值属性丢失无法触发移动语义五、std::future为什么需要std::futrue类?std::futrue是用来获取异步线程的返回值和异常的是主线程和子线程之间的结果传递通道一个子线程干完活想把结果告诉主线程怎么传需要用到std::futruestd::future就是异步任务的结果提货单拿着单子等线程跑完再取返回值特性1..get()只能调用一次一个future只能获取一次结果取完失效get()会阻塞当前线程直到异步任务完成2.bool vaild()判断当前future是否还持有结果3.void wait()只等待完成不获取结果4.wait_for(时间段)超时等待不卡死线程5.share()转为shared_future,允许多个线程同时获取结果调用多次.get()std::async:本质模板函数发起异步任务返回一个futurefuture对象.get()取任务返回值等于高级版thread自动管理不用join// async 是函数调用它 auto ft std::async(func); // ft 是 future 类实例.get() 是它的成员函数 int res ft.get();std::packaged_task:是一个模板类包装可调用对象的包装器把函数和future绑定在一起包装一函数执行任务后结果自动放进future,可以手动控制什么时候执行任务核心三步骤创建packaged_task包装你的函数从它身上取出future,调用task()执行任务future.get()拿结果#include iostream #include future using namespace std; int add(int a, int b) { return a b; } int main() { // 1. 创建 packaged_task包装函数 packaged_taskint(int, int) task(add); // 2. 取出 future futureint fu task.get_future(); // 3. 执行任务手动调用 task(10, 20); // 4. 拿结果 cout fu.get() endl; // 30 } async和packaged_task最大的区别async是自动执行packaged_task必须手动调动task()才可以std::promisestd::future:std::promiseT生产者主动存值、发数据std::futureT消费者等待取值、拿数据#include iostream #include thread #include future using namespace std; void setData(promiseint pro) { // 延迟模拟耗时操作 this_thread::sleep_for(chrono::seconds(1)); pro.set_value(666); // 存入数据 } int main() { promiseint p; futureint f p.get_future(); // 绑定成对 thread t(setData, move(p)); // 移动语义传promise cout 等待子线程赋值...\n; int res f.get(); // 阻塞等待拿到值才继续 cout 收到值 res endl;//res666 t.join(); return 0; } 二者成对绑定跨线程传值 / 传结果组件角色使用场景执行方式promisefuture主动发值 等待收值线程间手动传递数据手动 set_value 发packaged_taskfuture包装函数 获取函数返回值线程池封装任务调用任务自动回填结果asyncfuture一键异步执行简单异步任务自动开线程执行六、tuple定义作用就是打包一组数据元组能装任意多个、任意不同类型的数据容器长度固定混放不同类型的元素创建1.直接构造tupleint string,double t(1,张三,3.14);2.make_tuple自动推导(数据类型)auto t2make_tuple(20,李四,99.9);3.花括号初始化tupleint,intt3{5,6};特性1.取值get下标coutget0 (t);//下标从0开始2.批量赋值tieint a;string s;double d; tie(a,s,d)t;3.结构化绑定auto [num,str,val]make_tuple(10,abc,5.5);//变量对象存入4.tuple_sizedecltype(t)::value;获取元素个数七、std::apply作用把元组tuple的所有元素依次解包传入函数调用std::apply(函数元组)void add(int a,int b){ coutabendl; } int main(){ auto tmake_tuple(3,5); apply(add,t); return 0; }//搭配lambda auto tmake_tuple(10,20,30); apply([](int x,int y,int z){coutxyz;},t);八、lambda格式[捕获列表]参数列表mutable-返回值类型{函数体}[]捕获抓外部变量[]不捕获[]值捕获[]引用捕获[a]只捕获a值[a]只捕获a引用mutable值捕获也能修改副本-T :指定返回值可省略自动推导特性1.值捕获默认不能改加mutable可改脚本不改原变量用的是拷贝过来的旧值int x5; auto f[]()mutable{x100;}; f();//外面x还是52.lambda用外部变量有三类用法值捕获、引用捕获、传参九、万能引用定义模板中T万能引用int-纯右值引用万能引用既能接受左值也能接受右值完美转发的基础推导规则传左值T推导成T左值引用传右值T推导成T右值引用引用折叠 - - 只要有一个左值引用结果一定是左值引用遇左则左全右才右传入字面量T代入得T没有引用叠加直接右值引用十、智能指针作用自动管理堆内存无需手动delete底层依靠引用计数/独占所有权自动析构三大智能指针std::unique_ptr独占智能指针独占所有权同一个资源只能一个指针持有不能拷贝只能移动开销最小auto 变量make_unique类型(初始值);//模板函数make_unique干了两件事在堆上new构造对象打包返回unique_ptr unique_ptrint p1make_uniqueint (10); unique_ptrint p2move(p1);std::shared_ptr共享智能指针引用计数机制多人共享同一块内存计数0 自动释放内存可拷贝可赋值shared_ptrint p1(new int(10));//不推荐直接new构造异常不安全auto pmake_sharedint(666);//这里make_shared干了两件事在堆上new构造对象自动创建引用计数控制块打包返回shared_ptr一次性连续开辟两块内存对象控制块 //make_shared优点只分配一次内存异常安全不会内存泄漏 shared_ptrint p2p;//计数1 coutp.use_count();//查看引用计数std::weak_ptr弱指针这里不做详细讲解线程池代码#pragma once #include condition_variable #include functional #include future #include iostream #include mutex #include queue #include thread #include vector class pool { private: int thread_count; std::queuestd::functionvoid() works; std::vectorstd::thread thd; std::mutex mtx; std::condition_variable cv; bool runflag true; public: pool(int n 4) : thread_count(n) { for (int i 0; i thread_count; i) { thd.emplace_back([this]() { while (true) { std::functionvoid() task; { std::unique_lockstd::mutex lock(mtx); cv.wait(lock, [this] { return !works.empty() || !runflag; }); if (!runflag works.empty()) return; if (!works.empty()) { task std::move(works.front()); works.pop(); } } if (task) task(); } }); } } ~pool() { { std::unique_lockstd::mutex lock(mtx); runflag false; } cv.notify_all(); for (auto t : thd) { if (t.joinable()) t.join(); } } public: template typename F, typename... Args auto enqueue(F f, Args... args) - std::futuretypename std::invoke_resultF, Args...::type { using return_type typename std::invoke_resultF, Args...::type; auto task std::make_shared std::packaged_taskreturn_type()([func std::forwardF(f), args std::make_tuple( std::forwardArgs( args)...)]() mutable { return std::apply( func, args); }); std::futurereturn_type res task-get_future(); { std::unique_lockstd::mutex lock(mtx); works.emplace([task]() { (*task)(); }); } cv.notify_one(); return res; } };核心成员变量int thread_count; // 线程数量 std::queuestd::functionvoid() works; // 任务队列存“待执行函数” std::vectorstd::thread thd; // 线程池本体 std::mutex mtx; // 保护队列 std::condition_variable cv; // 线程等待/唤醒 bool runflag true; // 线程池运行标记任务队列用functionvoid():不管你丢什么任务全部包装成无参无返回的函数同一存储构造函数pool(int n 4) : thread_count(n) { for (int i 0; i thread_count; i) { thd.emplace_back([this]() { // 线程lambda while (true) { std::functionvoid() task; { std::unique_lockstd::mutex lock(mtx); // 等待有任务 或 线程池停止 cv.wait(lock, [this] { return !works.empty() || !runflag; }); // 退出条件停止无任务 if (!runflag works.empty()) return; // 取任务 task std::move(works.front()); works.pop(); } // 解锁后执行任务非常重要 if (task) task(); } }); } }[this]()捕获this:线程内部能访问县城池的队列、条件变量构造函数让线程具备等待 - 取任务 - 执行的能力没有任务-睡觉有任务/要退出-醒过来enqueue提交任务template typename F, typename... Args//接受任意类型的函数可调用函数任意数量参数 auto enqueue(F f, Args... args)//万能引用可接收左值和右值 - std::futuretypename std::invoke_resultF, Args...::type {//std::invoke推导f的返回值类型,future是未来取结果的容器 // 这一步相当于定义了一个 “万能快递箱”能装下任何类型的 “包裹函数”和 “配件参数”还承诺会返回一个 “取件码future” using return_type typename std::invoke_resultF, Args...::type; // 使用 packaged_task 封装任务并绑定返回值 auto task std::make_shared std::packaged_taskreturn_type()([func std::forwardF(f), args std::make_tuple( std::forwardArgs( args)...)]() mutable { return std::apply( func, args); // 把传入的可变参数比如 // 1,2打包成一个std::tuple元组把元组args拆成单个参数传给func执行 }); // 获取 future 对象获取 future给调用者返回 “取件码” std::futurereturn_type res task-get_future(); { std::unique_lockstd::mutex lock(mtx); works.emplace([task]() { (*task)(); }); // 将任务包装为 void() 类型 } cv.notify_one(); return res; }作用接受任意函数任意参数-包装成任务-扔进任务队列-返回future让你拿结果invoke_resultF,Args...::type:自动推导函数返回值类型超级强大函数声明auto enqueue(F f,Args... args)-std::futuretypename std::invoke_resultF,Args...::type这个函数主要干了什么invoke_result推导返回值类型、packaged_task封装任务、获取future、emplace把任务扔给队列、唤醒一个线程来干活、返回future实现了提交任意函数-自动包装-线程执行-返回结果一句话串完线程池流程外部传入函数可变参数——万能引用接收——完美转发打包tuple——lambda捕获保存——封装packaged_task生成future——任务入队——工作线程cv唤醒——move取出任务——apply解包执行——主线程future.get获取结果