C++ 17 20 中的线程类说明

在C++17和C++20标准中,多线程功能主要通过<thread><mutex><atomic><future><shared_mutex>等头文件提供的类和函数来实现。这些标准在C++11的基础上进行了扩展和优化,增强了多线程编程的能力。

以下是C++17和C++20中实现多线程的核心类及其原理、使用示例和注意事项:


一、核心多线程类及其实现原理

1. std::thread(线程类)

实现原理

  • std::thread是对操作系统原生线程(如POSIX pthread或Windows线程)的封装。
  • 在构造时,会调用系统API创建一个新的执行流(线程),并执行传入的可调用对象(函数、lambda、函数对象等)。
  • 每个std::thread对象管理一个独立的线程,支持移动语义(move semantics),但不支持拷贝。

C++17/C++20 新特性

  • C++17引入了std::jthread(在C++20中正式加入标准),支持自动join和协作式中断。

2. std::jthread(C++20新增)

实现原理

  • std::jthread是“joining thread”的缩写,是std::thread的增强版本。
  • 它在析构时会自动调用join(),避免了资源泄漏。
  • 支持协作式中断(cooperative interruption),通过std::stop_tokenstd::stop_source机制通知线程停止。

3. std::mutexstd::timed_mutexstd::recursive_mutex

实现原理

  • std::mutex是互斥锁,基于操作系统提供的互斥原语(如futex、CriticalSection)实现。
  • 保证同一时间只有一个线程可以进入临界区。
  • std::timed_mutex支持带超时的锁获取(try_lock_fortry_lock_until)。
  • std::recursive_mutex允许同一线程多次加锁,避免死锁。

4. std::shared_mutex(C++17引入)

实现原理

  • 支持共享/独占两种锁模式。
  • 多个读线程可以同时持有共享锁(shared_lock),但写线程必须独占(unique_lock)。
  • 基于读写锁(reader-writer lock)机制实现,适用于读多写少的场景。

5. std::atomic<T>(原子操作)

实现原理

  • 提供对特定类型T的原子操作(如loadstorefetch_add等)。
  • 底层依赖CPU的原子指令(如x86的LOCK前缀指令、ARM的LDREX/STREX)。
  • 避免使用锁的情况下实现线程安全的数据访问。

6. std::future 和 std::promise

实现原理

  • 用于异步操作的结果传递。
  • std::promise设置值,std::future获取该值。
  • 通过共享状态(shared state)在线程间传递数据,支持get()阻塞等待结果。

7. std::condition_variable

实现原理

  • std::mutex配合使用,实现线程间的等待/通知机制。
  • 线程可以等待某个条件成立(wait),其他线程在条件满足时通知(notify_onenotify_all)。

二、使用示例

示例1:std::thread 基本使用

#include <iostream>
#include <thread>
#include <vector>

void worker(int id) {
    for (int i = 0; i < 3; ++i) {
        std::cout << "Worker " << id << " working...\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 3; ++i) {
        threads.emplace_back(worker, i);
    }

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

    return 0;
}

注意:必须调用join()detach(),否则程序会std::terminate


示例2:std::jthread(C++20)自动join和中断

#include <iostream>
#include <thread>
#include <chrono>

void worker_with_stop(std::stop_token stoken) {
    for (int i = 0; i < 10; ++i) {
        if (stoken.stop_requested()) {
            std::cout << "Worker stopped.\n";
            return;
        }
        std::cout << "Working... " << i << "\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

int main() {
    std::jthread jt(worker_with_stop);

    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    // 请求停止
    jt.request_stop();

    // 析构时自动join
    return 0;
}

优势:无需手动join,支持安全中断。


示例3:std::shared_mutex 读写锁

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>

std::shared_mutex rw_mutex;
int data = 0;

void reader(int id) {
    for (int i = 0; i < 3; ++i) {
        std::shared_lock<std::shared_mutex> lock(rw_mutex);
        std::cout << "Reader " << id << " reads data: " << data << "\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
}

void writer(int id) {
    for (int i = 0; i < 2; ++i) {
        std::unique_lock<std::shared_mutex> lock(rw_mutex);
        data += id;
        std::cout << "Writer " << id << " writes data: " << data << "\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 3; ++i) {
        threads.emplace_back(reader, i);
    }
    for (int i = 1; i <= 2; ++i) {
        threads.emplace_back(writer, i);
    }

    for (auto& t : threads) {
        t.join();
    }
    return 0;
}

适用场景:读操作频繁,写操作较少。


示例4:std::atomic 原子操作

#include <iostream>
#include <thread>
#include <atomic>
#include <vector>

std::atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 1000; ++i) {
        counter.fetch_add(1, std::memory_order_relaxed);
    }
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment);
    }

    for (auto& t : threads) {
        t.join();
    }

    std::cout << "Final counter value: " << counter.load() << "\n";
    return 0;
}

注意:原子操作不保证顺序一致性,必要时使用更强的内存序(如std::memory_order_seq_cst)。


示例5:std::future 和 std::async

#include <iostream>
#include <future>
#include <chrono>

int heavy_computation() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    // 异步启动任务
    std::future<int> fut = std::async(std::launch::async, heavy_computation);

    std::cout << "Doing other work...\n";
    int result = fut.get(); // 阻塞等待结果
    std::cout << "Result: " << result << "\n";

    return 0;
}

注意std::async可能不会创建新线程(取决于std::launch::deferred策略)。


三、注意事项

  1. 资源管理

    • std::thread必须在销毁前调用join()detach(),否则程序终止。
    • 推荐使用std::jthread(C++20)避免此问题。
  2. 死锁预防

    • 避免嵌套锁或锁顺序不一致。
    • 使用std::lock()一次性锁定多个互斥量。
  3. 性能考虑

    • 频繁加锁影响性能,考虑无锁数据结构或原子操作。
    • std::shared_mutex适用于读多写少,否则可能不如普通互斥锁。
  4. 内存序(Memory Order)

    • 原子操作的内存序影响性能和正确性,合理选择memory_order
  5. 异常安全

    • 确保锁在异常发生时能正确释放,使用RAII(如std::lock_guard)。
  6. 线程局部存储

    • 使用thread_local关键字声明线程局部变量,避免共享。
  7. 避免数据竞争

    • 所有共享数据必须通过同步机制(锁、原子、消息传递)访问。

总结

类/机制用途C++标准
std::thread创建和管理线程C++11
std::jthread自动join和中断的线程C++20
std::mutex互斥锁C++11
std::shared_mutex读写锁C++17
std::atomic<T>原子操作C++11
std::future/promise异步结果传递C++11
std::condition_variable条件变量C++11

C++17和C++20在多线程方面增强了易用性和安全性(如std::shared_mutexstd::jthread),开发者应结合具体场景选择合适的同步机制,确保程序的正确性、性能和可维护性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

semicolon_helloword

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值