c++11的function、(bind、lambda绑定回调)、std::thread使用示例

function与lambda示例

function的作用就是把函数、函数对象、仿函数等统一封装成对象,取代函数指针,常用于回调,类似函数的多态(一个统一接口),对于解耦代码很友好。
而bind和lambda就是创建函数对象传给他的,如std::thread()就要传一个函数对象。(建议使用lambda,即可绑定匿名回调)

#include <functional>
#include <unordered_map>
#include <iostream>

class CommandHandler {
private:
    // 定义回调函数类型
    using Callback = std::function<void()>;
    // 存储命令和对应的回调函数
    std::unordered_map<int, Callback> callbacks;

public:
    // 不要这样写,违反了开闭原则,类内的代码不能随意改
    // void registerCallback() {
    //     callbacks[1] = []() { std::cout << "执行命令1" << std::endl; };
    //     callbacks[2] = []() { std::cout << "执行命令2" << std::endl; };
    //     callbacks[3] = []() { std::cout << "执行命令3" << std::endl; };
    // }

    // 注册回调函数
    void registerCallback(int cmd, Callback callback) {
        callbacks[cmd] = std::move(callback);
    }

    // 处理命令:查表执行回调
    void handleCommand(int cmd) {
        auto it = callbacks.find(cmd);
        if (it != callbacks.end()) {
            it->second();  
        } else {
            std::cout << "未知命令: " << cmd << std::endl;
        }
    }
};

// 使用示例
int main() {
    CommandHandler handler;

    // 注册各种命令的回调函数,用lambda传的
    handler.registerCallback(1, []() {
        std::cout << "执行回调1" << std::endl;
    });

    handler.registerCallback(2, []() {
        std::cout << "执行回调2" << std::endl;
    });

    handler.registerCallback(3, []() {
        std::cout << "执行回调3" << std::endl;
    });

    // 处理用户输入
    while (1) {
        std::cout << "请输入命令(1-3,0退出):";
        int cmd = 0;
        std::cin >> cmd;
        if (cmd == 0) break;
        handler.handleCommand(cmd);
    }
}

c++11线程库

c++11线程库:语言级别好处是跨平台,且不用-pthread

线程使用

#include <thread> 
std::thread t1(myfunc,2);这就是一个线程,且可传参
//或者用匿名函数
std::thread t([](int a){
		std::cout<< a << std::endl;
}, 2);
t1.detach();
//若是成员函数回调成员函数,就用lambda表达式:
class Test {
public:
    void func(int a) {
        std::thread t([&](){raft(a);}); //&捕获,捕获到了this指针,自然能使用类成员
        t.detach();
    }
private:
    void raft(int a) {}
};

c++20的jthread :自动join或detach,还可以手动t.request_stop(); 请求停止线程

互斥锁

锁的本质就是只能指针,构造函数里加锁,析构函数里解锁,所以出作用域就解锁了
注意: c++11的mutex、cv、sem都不能用于进程间同步,若要进程间同步:可以用共享内存+pthread_mutex/sem_t等,参考操作系统-进程篇

#include <mutex>
std::mutex mtx;
{
	//省略了模板定义,因为c++17后都可以自动推导了,如std::vector v = {1};
	//但一般不省,锁这里省掉不影响可读性。
    std::unique_lock lock(mtx);//lock是锁名,出作用域自动解锁,也可手动加解锁或设置加锁超时时间
    std::lock_guard lock(mtx); //更轻量级,只能自动解锁,功能少。
    xxx
}

读写锁

读共享,写独占,适用于读多写少的场景;posix的:pthread_rwlock_t

#include <shared_metex>
#include <mutex>
std::shared_metex sMtx;
{
	//获取读锁,自动解锁;若是想用数据,可以先把数据存下来,然后赶紧解锁。
	std::shared_lock lock(sMtx); 
	xxx;
}
{
	//获取写锁,自动解锁
	std::unique_lock lock(sMtx);  // 加写锁
	xxx
}
//也可手动加解锁,适合封装使用
std::shared_mutex rw_mtx;
rw_mtx.lock();			  //加写锁
rw_mtx.unlock();		  //释放写锁
rw_mtx.lock_shared(); 	  //加共享读锁
rw_mutex.unlock_shared(); //释放读锁

条件变量

配合锁使用,可参考C++线程池
posix的:pthread_cond_t cv;

#include <condition_variable> 
std::mutex mtx;
std::condition_variable cv;
{
	std::unique_lock lck(mtx); 
    //释放锁,等待notify
    cv.wait(lck);
    
    /* 	wait函数执行步骤:被唤醒->检查条件: 满足就获得锁返回,不满足继续沉睡
		这种写法很好,可以避免虚假唤醒	*/
	cv.wait(lock, [&](){ return stop || !tasks.empty(); }); 
	
    //释放锁,等待一定时间,超时自动醒
    cv_.wait_for(lock, std::chrono::milliseconds(connectTimeout_));

    cv.notify_all();//唤醒所有线程
    cv.notify_one();//唤醒一个线程获得锁
}

信号量

c++20, 一般也是配合锁使用;posix的:sem_t sem;
信号量代表了资源的数量,如同时访问数据库的最大线程数。

#include <iostream>
#include <unistd.h>
#include <thread>
#include <semaphore>

//本例信号量代表:最多10个线程同时访问worker函数
std::counting_semaphore<10> sem(2); //<10>: 表示信号量的上限,(2): 初始化信号量数

void worker(int id) {
    sem.acquire();  //若sem>0,就获取,sem-1; 若sem=0, 就阻塞,直到sem>0
    std::cout << "Worker " << id << " is working." << std::endl;
    sleep(1);       // 模拟工作
    std::cout << "Worker " << id << " is done." << std::endl;
    sem.release();  // 释放, sem+1
    //sem.release(5); //sem+5
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    std::thread t3(worker, 3);

    t1.join();
    t2.join();
    t3.join();
}

结果如图:
在这里插入图片描述
t1, t2是并发执行的,因为刚开始有2个信号量,而t3需要等他们release才获得sem。

原子类型

可以保证对单个变量操作的线程安全

#include <atomic>
//<T>只能是整数或指针
std::atomic<int> a;
std::atomic<Node*> p;
a++; //不用加锁
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值