再看此章节之前,先看手写C++线程池的代码部分
总览:
通过C++11线程池掌握C++常用新特性
1.腾讯面试:线程池引入无锁队列能否提升性能
2.通过线程池组件掌握C++新特性
3.C函数、C++类函数无缝扔给线程池处理
4.如何实现多参数任务的支持
5.如何等待任务执行返回结果
面试问题:
多进程与多线程的使用场景:
多线程之前存在数据强交互联系
多线程会简单一些
腾讯面试:线程池引入无锁队列能否提升性能
无锁队列 插入数据:--取数据 两端是有竞争
大部分时候优先考虑 mutex+condition+notify 阻塞唤醒队列。
设计知识点:
Thread
Mutex
Condition
Forward
Move
Bind
Future
packaged task
Lambda
代码流程
初始化线程池
启动线程
测试线程池
exec函数
1.多参数列表
template<class F,class....Args>//可变参数列表
auto exec(F &&f,Args&&...args)->std::future<decltype(f(args...))>
using RetType = decltype(f(args...));//推导返回值
执行线程
--->get()
atomic++与--代表正在任务执行的数量,是为了防止队列任务为空,直接释放线程池,但这是有的任务还没有执行完成。加入atomic进行判断
--->任务队列为空,没有正在执行的任务,退出线程池
ps:1.decltype:
ps:2.future、async、packaged_task的用法(异步执行)
futrue与async----->future_result.get()
func传入给async时,已经在运行了,相当于一个线程
此时这条语句(红色方框)不会阻塞,会继续运行。向下走,执行cout<<func()<<endl;
future与packaged_task(get_future)
packaged_task:实现相同功能
将func封装成一个packaged_task的对象,手动创建一个线程
总结:
P3:完美转发
forward
完美转发是 C++11 引入的一种技术,允许我们在模板函数中传递参数时,保持这些参数的值类别(左值或右值)。这对于泛型编程非常重要,因为它可以确保我们在调用其他函数时,能够以最有效的方式传递参数,避免不必要的拷贝和移动操作。
#include <iostream>
#include <utility>
void process(int& x) {
std::cout << "Lvalue: " << x << std::endl;
}
void process(int&& x) {
std::cout << "Rvalue: " << x << std::endl;
}
template<typename T>
void wrapper(T&& arg) {
// 完美转发
process(std::forward<T>(arg));
}
int main() {
int a = 10;
wrapper(a); // 传递左值
wrapper(20); // 传递右值
}
输出:Lvalue: 10 Rvalue: 20
wrapper
是一个模板函数,接受一个参数 arg
,其类型为 T&&
(通用引用)。
std::forward<T>(arg)
将 arg
以其原始值类别转发给 process
函数。
P4:多参数列表 (Variadic Templates)
#include <iostream>
#include <utility>
template<typename... Args>
void print(Args&&... args) {
(std::cout << ... << std::forward<Args>(args)) << std::endl; // 使用折叠表达式
}
int main() {
print(1, 2.5, "Hello, world!"); // 打印多个类型的参数
}
输出:1 2.5Hello, world!
结合完美转发和多参数列表的例子
#include <iostream>
#include <utility>
void process(int a, double b, const std::string& str) {
std::cout << "Int: " << a << ", Double: " << b << ", String: " << str << std::endl;
}
template<typename... Args>
void wrapper(Args&&... args) {
// 完美转发多个参数
process(std::forward<Args>(args)...);
}
int main() {
int x = 42;
double y = 3.14;
std::string greeting = "Hello";
wrapper(x, y, greeting); // 传递左值
wrapper(100, 2.71, "World!"); // 传递右值
}
Int: 42, Double: 3.14, String: Hello Int: 100, Double: 2.71, String: World!
小思考:
template<typename... Args>
void wrapper(Args&&... args)
与
template<typename T1,typename... Args>
void wrapper(T1 t1,Args&&... args)两种写法的区别
第一种写法:template<typename... Args> void wrapper(Args&&... args)
参数接收:该函数接受任意数量的参数。所有参数都包含在参数包 Args
中。
类型推导:通过 Args&&
,每种参数的值类别(左值或右值)都会被保留,允许完美转发。
用例:适用于需要处理一组参数并在内部进行转发或处理的场景。
#include <iostream>
#include <utility>
template<typename... Args>
void wrapper(Args&&... args) {
// 假设我们想打印所有参数
(std::cout << ... << std::forward<Args>(args)) << std::endl;
}
int main() {
int x = 10;
wrapper(x, 20, "Hello"); // 可以传递任意数量的参数
}
第二种写法:template<typename T1, typename... Args> void wrapper(T1 t1, Args&&... args)
特点:
参数接收:该函数接受一个特定类型 T1
的参数 t1
,然后接受任意数量的其他参数 Args
.
类型推导:t1
的类型是确定的,而 Args&&... args
仍然会保留值类别。
用例:适用于需要在处理参数时对第一个参数进行特定处理(例如,执行操作或打印)而对后续参数进行灵活处理的场景。
#include <iostream>
#include <utility>
template<typename T1, typename... Args>
void wrapper(T1 t1, Args&&... args) {
std::cout << "First parameter: " << t1 << std::endl; // 处理第一个参数
// 假设我们想打印所有后续参数
(std::cout << ... << std::forward<Args>(args)) << std::endl; // 打印后续参数
}
int main() {
int x = 10;
wrapper(x, 20, "Hello"); // 第一个参数是 x,后续参数是 20 和 "Hello"
}
P5:通用引用与通用引用!!!!
-
右值引用:在 C++11 中引入,以
Type&&
的形式表示,主要用来表示临时对象或可移动的对象(右值)。 -
通用引用:当模板类型参数以
如果传递的是左值,则T&&
的形式出现时(例如:template<typename T> void func(T&& param)
),T&&
被称为通用引用。它可以绑定到左值和右值。T
推导为左值引用类型,T&&
变为左值引用(Type&
)。如果传递的是右值,则T
推导为非引用类型,T&&
变为右值引用(Type&&
)。 -
#include <iostream> #include <utility> void process(int& x) { std::cout << "Lvalue: " << x << std::endl; } void process(int&& x) { std::cout << "Rvalue: " << x << std::endl; } template<typename T> void wrapper(T&& arg) { // 完美转发 process(std::forward<T>(arg)); } int main() { int a = 10; wrapper(a); // 传递左值 wrapper(20); // 传递右值 }
当你调用
wrapper(a)
时,arg
会被推导为int&
(左值引用),因此T
被推导为int&
,而T&&
变为int&
。在后续的std::forward<T>(arg)
中,std::forward
保持了arg
的左值属性。 当你调用wrapper(20)
时,arg
会被推导为int
(基本类型),因此T
被推导为int
,而T&&
变为int&&
。在后续的std::forward<T>(arg)
中,std::forward
将arg
作为一个右值传递给process
。