C++多线程学习笔记

C++11实现跨平台线程池

线程池:是一种线程的使用模式,它为了降低线程使用中频繁的创建和销毁所带来的资源消耗与代价。

通过创建一定数量的线程,让他们时刻准备就绪等待新任务的到达,而任务执行结束之后再重新回来继续待命。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <string>

#include <condition_variable>
#include <queue>
#include<functional>

class ThreadPool {
public:
    ThreadPool(int numThread) :stop(false){
        for (int i=0;i<numThread; i++){
            //lambda表达式
            threads.emplace_back([this] {
                while (true){
                    std::unique_lock<std::mutex> lock(mtx);
                    condition.wait(lock, [this] {
                        return !tasks.empty() || stop;
                    });

                    if (stop && tasks.empty()) {
                        return;
                    }

                    //取任务
                    std::function<void()> task(std::move(tasks.front()));
                    tasks.pop();
                    lock.unlock();

                    //完成任务
                    task();
                }
                });
        }
    }

    ~ThreadPool(){
        {
            std::unique_lock<std::mutex> lock(mtx);
            stop = true;
        }
        //通知队列所有任务取完
        condition.notify_all();
        //让所有任务执行完成
        for (auto& t : threads){
            t.join();
        }
    }

    //加任务
    template<typename F, typename... Args>
    void enqueue(F&& f, Args&&... args)
    {
        //函数绑定
        std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));

        {
            std::unique_lock<std::mutex> lock(mtx);
            tasks.emplace(std::move(task));
        }
     
        //通知线程
        condition.notify_one();
    }

private:
    std::vector<std::thread> threads;
    std::queue<std::function<void()>> tasks;

    //线程池符合生产消费者模式
    std::mutex mtx;
    std::condition_variable condition;

    bool stop;
};

int main() 
{
    ThreadPool pool(4);
    //加任务
    for (int i=0; i<8; i++)
    {
        pool.enqueue([i] {
            std::cout << "Task " << i << " is running in thread " << std::this_thread::get_id() << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << "Task " << i << " is done" << std::endl;
            });
    }

    return 0;
}

std::unique_lock

cpp 复制代码
std::unique_lock<std::mutex> lock(mtx);//创建一个互斥量封装锁
std::unique_lock<std::timed_mutex> lg(mtx, std::defer_lock); //lock
if (lg.try_lock_for(std::chrono::seconds(2))) 
 {
      std::this_thread::sleep_for(std::chrono::seconds(1));
       share_data++;
 }      
 //创建一个唯一锁,但不立即上锁,然后尝试在2秒内获取锁。如果获取成功,则执行if语句块内的代码;
 //如果超时(2秒内没获取到锁),则跳过if块(或者进入else分支,如果有的话)。

condition_variable 与其使用场景

条件变量是C++多线程编程中线程同步的核心工具,用于线程间的等待和通知机制。

cpp 复制代码
#include <string>

#include <condition_variable>
#include <queue>

std::queue<int> g_queue;
std::condition_variable g_cv;
std::mutex mtx;

void Producer()
{
    for (int i=0; i<10; i++)
    {
        {
            std::unique_lock<std::mutex> lock(mtx);
            g_queue.push(i);
            //通知消费者来领取任务
            g_cv.notify_one();
            std::cout << "Producer :" << i << std::endl;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void Consumer()
{
    while (true)
    {
        std::unique_lock<std::mutex> lock(mtx);

        bool isempty = g_queue.empty();
        //如果队列为空,就要等待
        g_cv.wait(lock, []() { return !g_queue.empty(); });
        int value = g_queue.front();
        g_queue.pop();

        std::cout << "Consumer :" << value << std::endl;
    }
}


int main() 
{
    std::thread producer_thread(Producer);
    std::thread consumer_thread(Consumer);
    producer_thread.join();
    consumer_thread.join();

    return 0;
}

std::function<>

function类似于auto,是C++标准库提供的一个通用可调用对象包装器,可以表示函数,函数对象,lambda表达式,反正有点抽象,但是使用简单:

cpp 复制代码
# include <iostream>
# include <functional>

typedef std::function<int(int, int)> comfun;

// 普通函数
int add(int a, int b) { return a + b; }

// lambda表达式
auto mod = [](int a, int b){ return a % b; };

// 函数对象类
struct divide{
    int operator()(int denominator, int divisor){
        return denominator/divisor;
    }
};

int main(){
	comfun a = add;
	comfun b = mod;
	comfun c = divide();
    std::cout << a(5, 3) << std::endl;
    std::cout << b(5, 3) << std::endl;
    std::cout << c(5, 3) << std::endl;
}

std::bind()

std::bind 是 C++11 标准库中的一个函数适配器(function adapter),用于部分应用(partial application)和参数绑定(parameter binding)。它创建一个新的可调用对象(callable object),将原函数的某些参数固定为特定值。

cpp 复制代码
#include <iostream>
#include <functional>

class A {
public:
    void fun_3(int k,int m) {
        std::cout << "print: k = "<< k << ", m = " << m << std::endl;
    }
};

void fun_1(int x,int y,int z) {
    std::cout << "print: x = " << x << ", y = " << y << ", z = " << z << std::endl;
}

void fun_2(int &a,int &b) {
    ++a;
    ++b;
    std::cout << "print: a = " << a << ", b = " << b << std::endl;
}

int main(int argc, char * argv[]) {
    //f1的类型为 function<void(int, int, int)>
    auto f1 = std::bind(fun_1, 1, 2, 3); 					//表示绑定函数 fun 的第一,二,三个参数值为: 1 2 3
    f1(); 													//print: x=1,y=2,z=3

    auto f2 = std::bind(fun_1, std::placeholders::_1, std::placeholders::_2, 3);
    //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别由调用 f2 的第一,二个参数指定
    f2(1, 2);												//print: x=1,y=2,z=3
 
    auto f3 = std::bind(fun_1, std::placeholders::_2, std::placeholders::_1, 3);
    //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别由调用 f3 的第二,一个参数指定
    //注意: f2  和  f3 的区别。
    f3(1, 2);												//print: x=2,y=1,z=3

    int m = 2;
    int n = 3;
    auto f4 = std::bind(fun_2, std::placeholders::_1, n); //表示绑定fun_2的第一个参数为n, fun_2的第二个参数由调用f4的第一个参数(_1)指定。
    f4(m); 													//print: a=3,b=4
    std::cout << "m = " << m << std::endl;					//m=3  说明:bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的,如m
    std::cout << "n = " << n << std::endl;					//n=3  说明:bind对于预先绑定的函数参数是通过值传递的,如n
    
    A a;
    //f5的类型为 function<void(int, int)>
    auto f5 = std::bind(&A::fun_3, &a, std::placeholders::_1, std::placeholders::_2); //使用auto关键字
    f5(10, 20);												//调用a.fun_3(10,20),print: k=10,m=20

    std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);
    fc(10, 20);   											//调用a.fun_3(10,20) print: k=10,m=20 

    return 0; 
}

可变参数模板函数

F:一个类型参数,代表第一个函数参数 f 的类型

Args...:可变类型参数包,代表后续所有参数的类型,可以是0个

cpp 复制代码
template< class F, class... Args >
template< typename F, typename... Args >
#include <iostream>

// 递归终止函数
void print() {
    std::cout << std::endl;
}

// 可变参数模板函数
template<typename T, typename... Args>
void print(T first, Args... args) {
    std::cout << first << " ";
    print(args...);  // 递归调用
}

int main() {
    // 测试
    print(1, 2, 3);                     // 输出: 1 2 3
    print("Hello", "World", 3.14);      // 输出: Hello World 3.14
    print('A', 42, "test", 2.5);        // 输出: A 42 test 2.5
    print();                            // 输出空行
    
    return 0;
}

std::forward

std::forward是C++11中引入的一个函数模板,用于实现完美转发(Perfect Forwarding)。它的作用是根据传入的参数,决定将参数以左值引用还是右值引用的方式进行转发。
std::forward与完美转发详解

详解enqueue函数

cpp 复制代码
//加任务
    template<typename F, typename... Args>
    void enqueue(F&& f, Args&&... args)
    {
        //函数绑定
        std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));

        {
            std::unique_lock<std::mutex> lock(mtx);
            tasks.emplace(std::move(task));
        }
     
        //通知线程
        condition.notify_one();
    }
cpp 复制代码
void enqueue(F&& f, Args&&... args) //实现了通用引用
//三种参数传递方式的对比
// 方式1:值传递(拷贝) - ❌ 最差
template<typename F, typename... Args>
void enqueue_copy(F f, Args... args) {
    // 每次调用都会复制 f 和所有 args
    std::function<void()> task(std::bind(f, args...));
    // ...
}

// 方式2:常量引用 - ❌ 不够好
template<typename F, typename... Args>
void enqueue_const(const F& f, const Args&... args) {
    // 不能接受右值,不能移动
    std::function<void()> task(std::bind(f, args...));
    // ...
}

// 方式3:通用引用(完美转发) - ✅ 最佳
template<typename F, typename... Args>
void enqueue(F&& f, Args&&... args) {
    // 完美转发,保持值类别
    std::function<void()> task(std::bind(
        std::forward<F>(f), 
        std::forward<Args>(args)...
    ));
    // ...
}

std::function<void()> task(std::bind(std::forward(f), std::forward(args)...));

cpp 复制代码
bind是将f函数,args参数包进行绑定后返回一个无返回值无参数的函数传给function<void()>
可以直接调用task()来实现功能,相当于调用了函数 f(args...)
eg:
		template<typename F, typename... Args>
		std::function<void()> make_task(F&& f, Args&&... args) {
		    return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
		}
		
		// 使用
		auto task = make_task(print_values, 42, 3.14, "hello");
		task();

std::move()

std::move 是 C++11 引入的一个强制类型转换工具,用于将左值转换为右值引用,从而启用移动语义。

可以避免不必要的深拷贝,减少内存和时间损耗

cpp 复制代码
#include <iostream>
#include <string>
#include <utility>  // std::move

int main() {
    std::string str1 = "Hello, World!";
    std::string str2 = "Goodbye";
    
    std::cout << "Before move:" << std::endl;
    std::cout << "str1: " << str1 << std::endl;  // "Hello, World!"
    std::cout << "str2: " << str2 << std::endl;  // "Goodbye"
    
    // 使用 std::move 将 str1 的内容移动到 str2
    str2 = std::move(str1);
    
    std::cout << "\nAfter move:" << std::endl;
    std::cout << "str1: " << str1 << std::endl;  // 未定义(通常是空)
    std::cout << "str2: " << str2 << std::endl;  // "Hello, World!"
    
    return 0;
}
相关推荐
比昨天多敲两行8 小时前
C++入门基础
开发语言·c++
知南x8 小时前
【正点原子STM32MP157 可信任固件TF-A学习篇】(2) STM32MP1 中的 TF-A
stm32·嵌入式硬件·学习·stm32mp157
YJlio8 小时前
Active Directory 工具学习笔记(10.0):AdExplorer / AdInsight / AdRestore 导读与场景地图
网络·笔记·学习
子夜江寒8 小时前
Python 学习-Day8-执行其他应用程序
python·学习
广东数字化转型8 小时前
工作备注笔记
笔记
ComputerInBook8 小时前
C++编程语言:标准库:第39章——本地化(语言环境)( Locales)(Bjarne Stroustrup)
c++·c++语言环境·c++ 本地化设置·c++ locale·c++ facet·语言特征
超高校级的作者9 小时前
博客摘录「 CentOS7 Fail2ban安装使用」2024年4月15日
笔记
●VON9 小时前
从单机应用到分布式调度:基于 HarmonyOS 构建车-空协同任务引擎
学习·华为·harmonyos·openharmony·开源鸿蒙
万变不离其宗_89 小时前
http学习笔记
笔记·学习