C++11线程池(c++常用特性)

再看此章节之前,先看手写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::forwardarg 作为一个右值传递给 process

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值