C++11并发支持库

@bit::Shadow
✧(≖ ◡ ≖✿

目录

1.线程库

2.std::chrono时间控制类

3.多种多样的锁

std::this_thread::

*使用lambda表达式处理线程调用函数

☆*禁拷贝的锁传参------ref()

atomic

内存序

condition_variable

async与future

async异步与线程的区别

[get VS wait](#get VS wait)

promise

shared_future

package_task


本篇的写作背景是为了"认识 了解"相应接口及性质,不是为了实践级复习。

写给读者:若追求详细、实践 请离开此文章。

1.<thread>线程库

2.std::chrono时间控制类

3.多种多样的锁

lock_guard

unique_lock

recursive_mutex // 递归互斥锁

try_lock // 尝试锁

try_to_lock

std::this_thread::

线程ID获取与线程定时休眠

*使用lambda表达式处理线程调用函数

☆*禁拷贝的锁传参------ref()

注意:x也必须是ref。---编译错误

atomic

整型与指针(常用于)

cpp 复制代码
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>

std::atomic<int> counter(0);   // 原子整型,初始值为 0

void worker() {
    for (int i = 0; i < 100000; ++i) {
        // 原子自增,等价于 counter.fetch_add(1)
        ++counter;
    }
}

int main() {
    std::vector<std::thread> threads;

    // 启动 10 个线程
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(worker);
    }

    // 等待所有线程结束
    for (auto& t : threads) {
        t.join();
    }

    // 期望结果:10 * 100000 = 1000000
    std::cout << "counter = " << counter.load() << std::endl;

    return 0;
}

原理

load意为读取原子变量中的值

同时也有

std::atomic::compare_exchange_strong()(使用较少)

内存序

在大量数据下(如果有必要的话)我们可以手动定制内存序(以提升效率):

condition_variable

条件变量 std::condition_variable

std::condition_variable::wait()

std::condition_variable::notify_one()

std::condition_variable::notify_all()

注意:条件变量使用unique_lock限定。(无互斥锁)

cpp 复制代码
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>

std::queue<int> q;                       // 共享队列
std::mutex mtx;                          // 保护队列的互斥锁
std::condition_variable cv;              // 条件变量
bool finished = false;                   // 生产者结束标志

// 生产者线程
void producer() {
    for (int i = 1; i <= 5; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟生产耗时

        {
            std::lock_guard<std::mutex> lock(mtx);
            q.push(i);
            std::cout << "生产者生产: " << i << std::endl;
        }   // 离开作用域自动解锁

        cv.notify_one();   // 通知一个等待的消费者
    }

    // 生产结束,设置标志并唤醒消费者
    {
        std::lock_guard<std::mutex> lock(mtx);
        finished = true;
    }
    cv.notify_one();
}

// 消费者线程
void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);

        // 等待条件成立:队列非空 或 生产结束
        // 使用 lambda 防止虚假唤醒(spurious wakeup)
        cv.wait(lock, [] { return !q.empty() || finished; });

        // 如果队列空了且生产结束,退出循环
        if (q.empty() && finished) {
            break;
        }

        // 取出数据
        int value = q.front();
        q.pop();
        std::cout << "消费者消费: " << value << std::endl;

        lock.unlock();  // 处理完数据后可以提前解锁(可选)
    }

    std::cout << "消费者结束" << std::endl;
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);

    t1.join();
    t2.join();

    return 0;
}

async与future

异步执行与延迟执行

async

auto的类型是std::future

cpp 复制代码
void task(const char* name) {
    std::cout << name << " 开始执行, 线程ID: "
              << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << name << " 完成" << std::endl;
}

int main() {
    std::cout << "主线程ID: " << std::this_thread::get_id() << std::endl;

    // 异步执行(新线程)
    auto f1 = std::async(std::launch::async, task, "异步任务");

    // 延迟执行(调用get时执行)
    auto f2 = std::async(std::launch::deferred, task, "延迟任务");

    std::cout << "主线程工作..." << std::endl;

    f1.get();   // 等待异步任务完成
    f2.get();   // 此时才执行延迟任务

    return 0;
}

async异步与线程的区别

"async"是线程的轻量化管理体系。

std::thread 给你一个裸线程,你自己管一切;std::async 给你一个"任务",库帮你管执行和结果。(我们使用时从其便捷的接口就可以得出)

future

std::future 是 C++11 引入的用于访问异步操作结果的类模板。它代表一个"未来会得到的结果",你可以通过它获取另一个线程(或延迟任务)计算出的值。

核心成员函数:get() wait() wait_for()/wait_until()

get VS wait

对比项 wait() get()
返回值 无(void) 任务的返回值
是否取走结果 ❌ 不取,结果仍留在 future 里 ✅ 取走,future 变为无效状态
能否重复调用 ✅ 可以多次 ❌ 只能调用一次
异常处理 不抛出任务里的异常 会重新抛出任务里的异常
对共享状态的影响 保持不变 使 future 失效(valid() == false)

promise

内部封装了future,主要用于get()、wait() 取结果。

set_value( )与set_value_at_thread_exit(),在不同时机设置get()的结果值。set_value() 直接唤醒get(若有) set_value_at_thread_exit()不会立即唤醒get()(若有)只有在任务结束后才会。

注意:二者均线程不安全

shared_future

实现了让多个线程(或从多个地方)能够安全地读取同一个异步结果。(get() . . .)

package_task

封装了promise、future的结合体


**用例线程池代码

cpp 复制代码
class ThreadPool {
public:
    // 构造函数,启动指定数量的工作线程
    ThreadPool(size_t threads) : stop(false) {
        for (size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                for (;;) {
                    std::function<void()> task;

                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        // 队列为空,始终wait阻塞
                        this->condition.wait(lock,
                            [this] { return this->stop || !this->tasks.empty(); });

                        // 线程池退出
                        if (this->stop && this->tasks.empty())
                            return;

                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }

                    task();
                }
            });
        }
    }

    // 向线程池添加任务
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args)
        -> std::future<typename std::result_of<F(Args...)>::type> {
        using return_type = typename std::result_of<F(Args...)>::type;

        //std::packaged_task <return_type()>* ptask = new std::packaged_task <return_type()>()
        
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );

        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);

            // 不允许在停止线程池后添加新任务
            if (stop)
                throw std::runtime_error("enqueue on stopped ThreadPool");

            tasks.emplace([task]() { (*task)(); });
        }
        condition.notify_one();
        return res;
    }

    // 析构函数,等待所有任务完成
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread& worker : workers)
            worker.join();
    }

private:
    // 工作线程集合
    std::vector<std::thread> workers;
    // 任务队列
    std::queue<std::function<void()>> tasks;

    // 同步原语
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};



本篇完

相关推荐
一技安身1 小时前
【java】JDK8 + JDK17 绿色解压共存方案-全程不修改任何系统环境变量
java·开发语言
ShineWinsu1 小时前
对于Redis:主从复制的解析
linux·数据库·c++·redis·缓存·面试·主从复制
hudd6682 小时前
JavaEE 初阶 - 多线程(初阶)
java·开发语言·java-ee
I Am a robert girl2 小时前
稀有事件估计的迭代去对齐:从源码视角拆解重要性采样新范式
开发语言·python·机器学习·重要性采样·蒙特卡洛方法·稀有事件估计
无忧.芙桃2 小时前
C++ 并查集详解:原理、实现与应用
c语言·c++·算法·leetcode
golang学习记2 小时前
rust开发,选VS Code还是RustRover?
开发语言·后端·rust
打工仔折腾 AI3 小时前
从零写一个CAD 05:以鼠标为中心的滚轮缩放,招法能复用但顺序不能反
开发语言·后端·python·性能优化·计算机外设·swift·ai agent 实战
CarIise3 小时前
Java实训阶段查漏补缺复习笔记1
java·开发语言·笔记
事圆则缓3 小时前
Kotlin 泛型方差实战:out、in、星投影与类型擦除
android·开发语言·kotlin