BLOG

Record, summarize, and improve.

C++并发支持库

1. 线程(Thread)

C++11 引入了 std::thread,用于在程序中创建并管理线程。

  • std::thread: 用于启动新线程。
    • std::thread t(func, args...): 创建并启动一个新线程,执行 func,传递参数 args
    • t.join(): 阻塞主线程,直到线程 t 完成。
    • t.detach(): 分离线程,主线程继续执行,线程 t 在后台运行。
#include <iostream>
#include <thread>

void threadFunction() {
    std::cout << "线程正在运行" << std::endl;
}

int main() {
    std::thread t(threadFunction);  // 创建并启动线程
    t.join();  // 等待线程完成
    return 0;
}

std::thread t(threadFunction); 创建并启动一个新线程,该线程执行 threadFunction()

t.join(); 等待线程 t 结束。

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

void backgroundTask() {
    std::this_thread::sleep_for(std::chrono::seconds(3));
    std::cout << "后台任务完成" << std::endl;
}

int main() {
    std::thread t(backgroundTask);
    t.detach();  // 分离线程,不等待其完成
    std::cout << "主线程继续运行" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    return 0;
}

t.detach(); 将线程 t 从主线程分离,允许其在后台运行,主线程不等待它完成。

#include <iostream>
#include <future>

int computeSum(int a, int b) {
    return a + b;
}

int main() {
    std::future<int> result = std::async(computeSum, 5, 10);
    std::cout << "结果: " << result.get() << std::endl;  // 等待并获取返回值
    return 0;
}

std::async 启动异步任务,并通过 future 对象获取返回结果。

2. 互斥(Mutex)

为了防止多个线程同时访问共享数据,C++ 提供了互斥机制。

  • std::mutex: 用于提供独占的线程锁。
    • lock(): 显式加锁互斥锁。
    • unlock(): 解锁互斥锁。
    • try_lock(): 尝试加锁互斥锁,成功返回 true,否则返回 false
  • std::recursive_mutex: 支持同一线程多次加锁的递归互斥锁。
1. 基本锁 (lock()unlock())

这是 std::mutex 最基本的使用方式,通过显式调用 lock()unlock() 方法对互斥量进行加锁和解锁。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int counter = 0;

void increment() {
    mtx.lock();        // 加锁
    ++counter;         // 共享资源操作
    mtx.unlock();      // 解锁
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

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

    std::cout << "Counter: " << counter << std::endl;
    return 0;

解释:在访问共享资源时,显式调用 mtx.lock() 加锁,操作完成后调用 mtx.unlock() 释放锁。

2. 使用 std::lock_guard 自动管理锁

std::lock_guard 是一种RAII机制的锁管理方式,确保在作用域结束时自动解锁,避免因异常或提前退出而未解锁的情况。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int counter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);  // 自动加锁解锁
    ++counter;
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

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

    std::cout << "Counter: " << counter << std::endl;
    return 0;
}

解释std::lock_guard 在创建时加锁,作用域结束时自动解锁,防止遗漏解锁的错误。

3. 使用 std::unique_lock 提供更灵活的锁管理

std::unique_lock 提供了比 std::lock_guard 更灵活的锁管理方式,可以在需要时手动加锁或解锁。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void task(bool condition) {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);  // 延迟加锁
    if (condition) {
        lock.lock();  // 根据条件手动加锁
        std::cout << "锁已加锁" << std::endl;
    } else {
        std::cout << "未加锁" << std::endl;
    }
}

int main() {
    std::thread t1(task, true);
    std::thread t2(task, false);

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

    return 0;
}

解释std::unique_lock 支持延迟加锁 (std::defer_lock),以及根据条件手动加锁或解锁的灵活性。

4. try_lock() 非阻塞尝试加锁

try_lock() 尝试加锁,如果锁可用,则成功加锁并返回 true,否则立即返回 false,不会阻塞线程。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void try_lock_example() {
    if (mtx.try_lock()) {  // 尝试加锁
        std::cout << "成功获取锁" << std::endl;
        mtx.unlock();
    } else {
        std::cout << "无法获取锁" << std::endl;
    }
}

int main() {
    std::thread t1(try_lock_example);
    std::thread t2(try_lock_example);

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

    return 0;
}

解释:如果一个线程获取到锁,它会执行相关代码;否则它会立即返回,避免阻塞。

5. 递归锁 (std::recursive_mutex)

std::recursive_mutex 允许同一线程多次获取同一把锁,而不会发生死锁。

#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex rec_mtx;

void recursiveFunction(int count) {
    if (count <= 0) return;
    rec_mtx.lock();
    std::cout << "递归函数,计数: " << count << std::endl;
    recursiveFunction(count - 1);
    rec_mtx.unlock();
}

int main() {
    std::thread t1(recursiveFunction, 5);
    t1.join();
    return 0;
}

解释:递归锁允许同一个线程多次加锁,但必须同样次数的解锁。

3. 锁管理(Locks)

为了解决手动管理锁的问题,C++ 提供了自动管理锁的机制。

  • std::lock_guard: 在作用域内管理互斥锁,当 lock_guard 对象销毁时,自动解锁。
  • std::unique_lock: 提供更灵活的锁管理机制,可以显式解锁和重新加锁。

4. 条件变量(Condition Variables)

用于线程之间的同步,允许一个线程等待条件发生,然后继续执行。

  • std::condition_variable: 通知等待线程某个条件已满足。
    • wait(lock): 释放锁并进入等待状态,直到被唤醒。
    • notify_one(): 唤醒一个等待中的线程。
    • notify_all(): 唤醒所有等待中的线程。
1. 基本使用 (wait()notify_one())

最简单的用法是通过 wait() 使线程等待,直到另一个线程通过 notify_one() 唤醒它。

用法:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lck(mtx);
    while (!ready) {  // 线程进入等待状态,直到 `ready` 为 `true`
        cv.wait(lck);
    }
    std::cout << "线程 " << id << std::endl;
}

void go() {
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_all();  // 唤醒所有等待线程
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i) {
        threads[i] = std::thread(print_id, i);
    }

    std::this_thread::sleep_for(std::chrono::seconds(1));
    go();

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

解释:

  • cv.wait(lck) 让线程等待,直到条件满足(ready == true)。
  • cv.notify_all() 唤醒所有等待中的线程。
2. 带条件检查的 wait()

std::condition_variablewait() 也可以接受一个谓词,用来简化条件检查逻辑。

用法:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait(lck, [] { return ready; });  // 使用条件谓词简化逻辑
    std::cout << "线程 " << id << std::endl;
}

void go() {
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_all();
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i) {
        threads[i] = std::thread(print_id, i);
    }

    std::this_thread::sleep_for(std::chrono::seconds(1));
    go();

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

解释:

  • 使用 wait(lck, predicate),避免显式的循环和条件检查,wait() 会自动在谓词返回 true 时停止等待。
3. 单线程通知 (notify_one())

当你只想唤醒一个等待中的线程时,可以使用 notify_one()

用法:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait(lck, [] { return ready; });
    std::cout << "线程 " << id << " 唤醒" << std::endl;
}

void go() {
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_one();  // 唤醒一个等待的线程
}

int main() {
    std::thread t1(print_id, 1);
    std::thread t2(print_id, 2);

    std::this_thread::sleep_for(std::chrono::seconds(1));
    go();

    t1.join();
    t2.join();
    return 0;
}

解释:

  • cv.notify_one() 只会唤醒一个等待的线程,而非全部。
4. 超时等待 (wait_for()wait_until())

可以让线程等待指定的时间间隔后继续执行,超时后线程将被自动唤醒,即使条件不满足。

用法:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lck(mtx);
    if (cv.wait_for(lck, std::chrono::seconds(2), [] { return ready; })) {
        std::cout << "线程 " << id << " 被唤醒" << std::endl;
    } else {
        std::cout << "线程 " << id << " 超时" << std::endl;
    }
}

void go() {
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟工作
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_all();  // 唤醒所有线程
}

int main() {
    std::thread t1(print_id, 1);
    std::thread t2(print_id, 2);

    go();

    t1.join();
    t2.join();
    return 0;
}

解释:

  • cv.wait_for(lck, duration, predicate) 会等待指定的时间 duration,如果时间内条件满足,则继续执行,否则超时并返回 false
5. 典型的生产者-消费者模式

condition_variable 常用于生产者-消费者模式中,用来同步生产者和消费者对缓冲区的访问。

用法:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> buffer;
const int maxSize = 5;

void producer() {
    int value = 0;
    while (true) {
        std::unique_lock<std::mutex> lck(mtx);
        cv.wait(lck, [] { return buffer.size() < maxSize; });  // 等待缓冲区有空位
        buffer.push(value++);
        std::cout << "生产者生产: " << value << std::endl;
        cv.notify_all();  // 通知消费者
    }
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lck(mtx);
        cv.wait(lck, [] { return !buffer.empty(); });  // 等待缓冲区有数据
        int value = buffer.front();
        buffer.pop();
        std::cout << "消费者消费: " << value << std::endl;
        cv.notify_all();  // 通知生产者
    }
}

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

    t1.join();
    t2.join();
    return 0;
}

解释:

  • 生产者等待缓冲区有空间,消费者等待缓冲区有数据,notify_all() 在每次生产或消费后通知对方线程继续工作。

5. 原子操作(Atomic Operations)

C++提供了 std::atomic 类型,支持对基本数据类型的无锁原子操作,避免了使用互斥锁的开销。

  • std::atomic: 提供原子操作的变量。
    • load(): 获取原子变量的值。
    • store(): 设置原子变量的值。
    • exchange(): 交换两个原子变量的值。
    • compare_exchange_weak()compare_exchange_strong():原子比较和交换操作。
1. 基本的原子操作

std::atomic 提供了基本的数据类型操作,如读取、写入、加法等。下面是如何使用 std::atomic 来处理基本的读写操作。

#include <iostream>
#include <atomic>
int main() {
    std::atomic<int> atomicCounter(0);  // 定义一个原子变量并初始化为0

    // 读取值
    int value = atomicCounter.load();   // atomicCounter的值是0
    std::cout << "初始值: " << value << std::endl;

    // 写入值
    atomicCounter.store(5);             // 将atomicCounter的值设置为5
    std::cout << "更新值: " << atomicCounter.load() << std::endl;

    return 0;
}

解释:

  • load():读取原子变量的值。
  • store():写入一个新值到原子变量。
2. 原子加法和减法

std::atomic 提供了 fetch_add()fetch_sub() 方法,可以对变量进行原子加法和减法操作。

#include <iostream>
#include <atomic>
int main() {
    std::atomic<int> atomicCounter(0);

    // 原子加法操作
    atomicCounter.fetch_add(10);        // 加10
    std::cout << "加法后: " << atomicCounter.load() << std::endl;

    // 原子减法操作
    atomicCounter.fetch_sub(4);         // 减4
    std::cout << "减法后: " << atomicCounter.load() << std::endl;

    return 0;
}

解释:

  • fetch_add(x):对原子变量加上 x
  • fetch_sub(x):对原子变量减去 x
3. 原子交换操作 (exchange)

exchange() 用于将原子变量的值替换为新值,并返回旧值。这是一个原子操作,保证在多线程环境下不会出现竞争条件。

#include <iostream>
#include <atomic>
int main() {
    std::atomic<int> atomicCounter(10);

    // 交换操作
    int oldValue = atomicCounter.exchange(20);
    std::cout << "旧值: " << oldValue << ", 新值: " << atomicCounter.load() << std::endl;

    return 0;
}

解释:

  • exchange(newValue):将原子变量的值设置为 newValue,并返回交换前的旧值。
4. 原子比较并交换 (compare_exchange_weakcompare_exchange_strong)

compare_exchange_weak()compare_exchange_strong() 是用于原子比较并交换的操作,比较当前值是否等于期望值,如果是则设置为新值。

#include <iostream>
#include <atomic>
int main() {
    std::atomic<int> atomicCounter(10);
    int expected = 10;
    int desired = 20;

    // 比较并交换操作
    if (atomicCounter.compare_exchange_strong(expected, desired)) {
        std::cout << "交换成功,新值: " << atomicCounter.load() << std::endl;
    } else {
        std::cout << "交换失败,当前值: " << atomicCounter.load() << std::endl;
    }

    return 0;
}

解释:

  • compare_exchange_strong(expected, newValue):如果原子变量当前值等于 expected,则将其设置为 newValue,并返回 true;否则返回 false
  • compare_exchange_weak():与 compare_exchange_strong() 类似,但可能在某些平台上被允许发生伪失败,更适合用于循环中。
5. 原子标志 (std::atomic_flag)

std::atomic_flag 是最简单的原子类型,只能存储布尔类型,支持 test_and_set()clear() 操作,适用于实现简单的锁。

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

std::atomic_flag lock = ATOMIC_FLAG_INIT;

void critical_section(int id) {
    while (lock.test_and_set(std::memory_order_acquire)) {
        // 忙等待,直到锁被释放
    }
    std::cout << "线程 " << id << " 进入临界区" << std::endl;
    lock.clear(std::memory_order_release);  // 释放锁
}

int main() {
    std::thread t1(critical_section, 1);
    std::thread t2(critical_section, 2);

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

    return 0;
}

解释:

  • test_and_set():设置标志并返回之前的值,常用于实现简易自旋锁。
  • clear():清除标志,释放锁。

6. 期望(Futures)与承诺(Promises)

用于线程间的值传递和异步任务的结果获取。

  • std::promise: 一个线程可以通过 std::promise 向另一个线程传递值或异常。
    • set_value(): 设置值。
    • set_exception(): 设置异常。
  • std::future: 用于获取异步操作的结果。
    • get(): 获取值(阻塞,直到结果准备好)。
    • wait(): 等待结果准备好。
  • std::async: 启动一个异步任务,返回 std::future,用于获取任务的结果。
    • std::async(std::launch::async, func, args...): 异步执行任务 func
1. 基本的 std::promisestd::future 用法

std::promise 是用来设置某个值的,std::future 是用来获取这个值的。可以通过 std::promise 设置值,然后通过关联的 std::future 获取这个值。

用法:

#include <iostream>
#include <thread>
#include <future>
void setPromiseValue(std::promise<int>& p) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    p.set_value(10);  // 设置 promise 的值
}

int main() {
    std::promise<int> p;         // 创建一个 promise
    std::future<int> f = p.get_future();  // 通过 promise 获取 future

    std::thread t(setPromiseValue, std::ref(p));  // 在另一个线程中设置值
    std::cout << "等待结果..." << std::endl;

    int result = f.get();  // 阻塞等待,直到结果设置好
    std::cout << "结果: " << result << std::endl;

    t.join();
    return 0;
}

解释:

  • std::promise<int> p; 创建一个 promise,用于存储异步结果。
  • std::future<int> f = p.get_future(); 获取与 promise 相关联的 future
  • f.get() 阻塞主线程,直到 promise 设置好值,并返回结果。
2. 使用 std::async 创建异步任务

std::async 启动一个异步任务,并返回一个 std::future,通过它可以在任务完成后获取结果。

用法:

#include <iostream>
#include <future>
int asyncTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 模拟耗时任务
    return 42;
}

int main() {
    std::future<int> f = std::async(std::launch::async, asyncTask);  // 启动异步任务
    std::cout << "等待异步任务完成..." << std::endl;

    int result = f.get();  // 获取异步任务结果
    std::cout << "异步任务结果: " << result << std::endl;

    return 0;
}

解释:

  • std::async(std::launch::async, asyncTask) 创建并启动一个异步任务,返回一个 std::future 对象。
  • f.get() 用于获取任务结果,阻塞直到任务完成。
3. 检查任务状态 (future_status)

std::future 提供了非阻塞的 wait_for() 方法,可以用来检测任务是否完成或超时。

用法:

#include <iostream>
#include <future>
#include <chrono>
int asyncTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    std::future<int> f = std::async(std::launch::async, asyncTask);

    if (f.wait_for(std::chrono::seconds(1)) == std::future_status::timeout) {
        std::cout << "任务超时,继续等待..." << std::endl;
    }

    int result = f.get();
    std::cout << "异步任务结果: " << result << std::endl;

    return 0;
}

解释:

  • f.wait_for(std::chrono::seconds(1)) 检查异步任务是否在1秒内完成。
  • 返回值可以是 std::future_status::timeout(超时)、ready(任务完成)或 deferred(延迟任务)。
4. 共享的 future (std::shared_future)

std::shared_future 允许多个线程共享一个 future 的结果。

用法:

#include <iostream>
#include <future>
int asyncTask() {
    return 42;
}

int main() {
    std::future<int> f = std::async(std::launch::async, asyncTask);
    std::shared_future<int> shared_f = f.share();  // 将 future 转换为 shared_future

    std::cout << "线程1结果: " << shared_f.get() << std::endl;
    std::cout << "线程2结果: " << shared_f.get() << std::endl;  // 可以多次调用

    return 0;
}

解释:

  • std::shared_future 允许多个线程安全地获取同一个结果,可以多次调用 get()
5. 使用 std::promise 传递异常

除了传递值,std::promise 还可以传递异常,future 会捕获并处理这些异常。

用法:

#include <iostream>
#include <thread>
#include <future>
void setPromiseException(std::promise<int>& p) {
    try {
        throw std::runtime_error("出错了");
    } catch (...) {
        p.set_exception(std::current_exception());  // 设置异常
    }
}

int main() {
    std::promise<int> p;
    std::future<int> f = p.get_future();

    std::thread t(setPromiseException, std::ref(p));

    try {
        int result = f.get();  // 获取异常
    } catch (const std::exception& e) {
        std::cout << "捕获到异常: " << e.what() << std::endl;
    }

    t.join();
    return 0;
}

解释:

  • p.set_exception(std::current_exception()) 捕获异常并将其传递给 future
  • f.get() 会重新抛出异常,允许调用者捕获和处理。

7. 任务组(Task Group)

C++20 引入了协程和任务组支持:

  • std::jthread: 与 std::thread 类似,但自动管理线程的加入(join),不需要手动调用 join()detach()
  • std::latchstd::barrier: 提供线程之间的同步机制,使得一组线程可以在某个点上同步执行。
1. 异步任务组 (std::async)

std::async 可以用于启动多个异步任务,并将它们组合在一起。通过使用 std::future,我们可以等待所有任务完成。

用法:

#include <iostream>
#include <future>
#include <vector>
int task1() {
    return 1;
}

int task2() {
    return 2;
}

int main() {
    // 启动异步任务
    std::future<int> f1 = std::async(std::launch::async, task1);
    std::future<int> f2 = std::async(std::launch::async, task2);

    // 获取结果
    int result1 = f1.get();
    int result2 = f2.get();

    std::cout << "结果: " << result1 + result2 << std::endl;

    return 0;
}

解释:

  • 使用 std::async 创建多个任务,通过 std::future 获取结果。
2. 线程池任务组

可以通过自定义的线程池或使用外部库(如 BoostIntel TBB)实现任务组。线程池管理多个任务,并在后台分发和执行。

用法:

#include <iostream>
#include <thread>
#include <vector>
#include <functional>
void task1() {
    std::cout << "执行任务1" << std::endl;
}

void task2() {
    std::cout << "执行任务2" << std::endl;
}

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

    // 启动多个线程任务
    tasks.push_back(std::thread(task1));
    tasks.push_back(std::thread(task2));

    // 等待所有任务完成
    for (auto& t : tasks) {
        t.join();
    }

    return 0;
}

解释:

  • 使用 std::thread 来启动多个任务并等待完成。这里模拟了任务组的功能。
3. 使用 std::futurestd::packaged_task

std::packaged_task 是另一种管理并行任务的方式,可以与 std::future 结合使用。

用法:

#include <iostream>
#include <future>
#include <vector>
int task1() {
    return 1;
}

int task2() {
    return 2;
}

int main() {
    std::packaged_task<int()> pt1(task1);
    std::packaged_task<int()> pt2(task2);

    std::future<int> f1 = pt1.get_future();
    std::future<int> f2 = pt2.get_future();

    // 启动任务
    pt1();
    pt2();

    // 获取结果
    int result1 = f1.get();
    int result2 = f2.get();

    std::cout << "总和: " << result1 + result2 << std::endl;

    return 0;
}

解释:

  • std::packaged_task 封装了任务函数,并与 std::future 结合获取结果。
4. 组合多个异步任务的结果

可以通过启动多个异步任务,并使用 std::future 来组合任务组的结果。

用法:

#include <iostream>
#include <future>
#include <vector>
int task(int x) {
    return x * 2;
}

int main() {
    std::vector<std::future<int>> futures;

    // 启动多个任务
    for (int i = 1; i <= 5; ++i) {
        futures.push_back(std::async(std::launch::async, task, i));
    }

    // 收集所有结果
    int sum = 0;
    for (auto& f : futures) {
        sum += f.get();
    }

    std::cout << "结果总和: " << sum << std::endl;

    return 0;
}

解释:

  • 多个异步任务的结果可以通过 std::future 组合,依次获取每个任务的结果并进行处理。

8. 协程(Coroutines)

C++20 引入了协程,可以使函数在执行过程中暂停并恢复,提供更轻量的并发支持。

  • co_await:用于暂停协程,直到某个操作完成。
  • co_yield:从协程中返回一个值,并暂停协程。
  • co_return:用于从协程中返回最终结果。
1. 基础协程 (co_return)

协程可以在执行中暂停并在后续某个时间恢复。这可以通过返回一个特殊类型来支持。

用法:

#include <iostream>
#include <coroutine>
struct MyCoroutine {
    struct promise_type {
        MyCoroutine get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };
};

MyCoroutine simpleCoroutine() {
    std::cout << "开始协程" << std::endl;
    co_return;
}

int main() {
    auto coro = simpleCoroutine();
    std::cout << "结束协程" << std::endl;
    return 0;
}

解释:

  • co_return 用于从协程中返回,协程在该点完成。
  • promise_type 是协程的承诺对象,控制协程的生命周期。
2. co_await 用于异步等待

co_await 可以用于等待异步操作完成,而不会阻塞当前线程。

用法:

#include <iostream>
#include <coroutine>
#include <chrono>
#include <thread>
struct MyTask {
    struct promise_type {
        MyTask get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };

    bool await_ready() { return false; }
    void await_suspend(std::coroutine_handle<>) {
        std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟等待
    }
    void await_resume() {}
};

MyTask delay() {
    std::cout << "开始等待..." << std::endl;
    co_await MyTask{};
    std::cout << "等待结束" << std::endl;
}

int main() {
    delay();
    std::cout << "主线程继续" << std::endl;
    return 0;
}

解释:

  • co_await 用于暂停协程执行,直到条件满足(如异步操作完成)。
  • await_ready()await_suspend()await_resume() 控制协程的挂起与恢复。
3. 生成器模式 (co_yield)

co_yield 允许协程返回多个值而不终止,通过协程生成器可以一次返回一个值。

用法:

#include <iostream>
#include <coroutine>
struct Generator {
    struct promise_type {
        int current_value;
        Generator get_return_object() { return Generator{std::coroutine_handle<promise_type>::from_promise(*this)}; }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        std::suspend_always yield_value(int value) {
            current_value = value;
            return {};
        }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };

    std::coroutine_handle<promise_type> coro;

    Generator(std::coroutine_handle<promise_type> h) : coro(h) {}
    ~Generator() { coro.destroy(); }
    bool next() { coro.resume(); return !coro.done(); }
    int current() { return coro.promise().current_value; }
};

Generator sequence(int start, int end) {
    for (int i = start; i <= end; ++i) {
        co_yield i;
    }
}

int main() {
    auto gen = sequence(1, 5);
    while (gen.next()) {
        std::cout << gen.current() << std::endl;
    }
    return 0;
}

解释:

  • co_yield 暂停协程并返回一个值,协程可以在后续被恢复并继续执行。
  • 生成器可以用于生成序列数据,例如迭代器的实现。
4. std::future 结合的异步任务

可以将协程与 std::future 结合,用来处理异步任务并获取结果。

用法:

#include <iostream>
#include <future>
#include <coroutine>
std::future<void> asyncTask() {
    std::cout << "开始异步任务" << std::endl;
    co_await std::async([] {
        std::this_thread::sleep_for(std::chrono::seconds(2));
    });
    std::cout << "异步任务完成" << std::endl;
}

int main() {
    auto task = asyncTask();
    task.get();  // 等待任务完成
    std::cout << "任务结束" << std::endl;
    return 0;
}

解释:

  • co_await 可与 std::future 结合使用,实现异步任务的等待。
  • 这种模式类似于其他语言中的 async/await 模式。
5. 嵌套协程

一个协程可以 co_await 另一个协程的结果,实现嵌套调用。

用法:

#include <iostream>
#include <coroutine>
struct Task {
    struct promise_type {
        Task get_return_object() { return Task{std::coroutine_handle<promise_type>::from_promise(*this)}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };

    std::coroutine_handle<promise_type> coro;
    Task(std::coroutine_handle<promise_type> h) : coro(h) {}
    ~Task() { coro.destroy(); }
};

Task nestedTask() {
    std::cout << "执行嵌套协程" << std::endl;
    co_return;
}

Task outerTask() {
    std::cout << "开始外层协程" << std::endl;
    co_await nestedTask();  // 嵌套调用
    std::cout << "结束外层协程" << std::endl;
}

int main() {
    auto task = outerTask();
    return 0;
}

解释:

  • co_await 可以用于等待另一个协程的执行结果,实现嵌套协程。

C++线程间同步方式

  1. 互斥锁(Mutex)
    • 互斥锁是一种最常见的同步机制,用于确保在同一时间只有一个线程能够访问共享资源。当一个线程锁定互斥锁时,其他线程必须等待,直到锁被释放。
  2. 条件变量(Condition Variable)
    • 条件变量用于在一个线程等待另一个线程满足某个条件之前进入等待状态。一个线程可以通过条件变量等待,而另一个线程可以通过条件变量来通知等待的线程条件已经满足,从而唤醒它们。
  3. 信号量(Semaphore)
    • 信号量是一种用于多线程同步的计数机制,它可以控制同时访问共享资源的线程数量。信号量可以用于解决生产者-消费者问题等场景。
  4. 屏障(Barrier)
    • 屏障用于确保多个线程在某个点上同步,只有所有线程都到达这个点后,它们才会继续执行。屏障通常用于分阶段的并行计算。
  5. 原子操作(Atomic Operations)
    • 原子操作是一种在多线程环境下执行的不可分割的操作。它们可以确保在多个线程之间对共享变量进行安全的读取和更新,而不需要额外的互斥锁。
  6. 读写锁(Read-Write Lock)
    • 读写锁允许多个线程同时读取共享资源,但只有一个线程可以写入共享资源。这可以提高读取操作的并发性。
  7. 消息队列(Message Queue)
    • 消息队列允许多个线程通过向队列发送和接收消息来进行通信和同步。消息队列通常用于生产者-消费者模型和事件驱动编程。
  8. 条件同步(Conditional Synchronization)
    • 条件同步是一种更高级的同步机制,它允许线程在某些条件满足时执行特定操作,而在条件不满足时进入等待状态。这可以用于复杂的线程协作和同步需求。
  9. 读写自旋锁(Read-Write Spinlock):类似于读写锁,但使用自旋方式等待锁的释放而不是进入休眠状态。
  10. 计数信号量(Counting Semaphore):与二进制信号量不同,计数信号量允许计数的增减,用于控制多个线程访问共享资源。
  11. 定时器(Timer):用于在特定时间间隔内触发回调函数,可用于定期执行任务。
  12. 并行容器(Parallel Containers):C++标准库的并行模块提供了一些并行容器,如std::parallel::vectorstd::parallel::map,用于支持在多线程环境中安全地操作数据结构。
  13. 协程(Coroutines):C++20引入了协程,允许线程在函数之间切换执行,以实现非抢占式多任务。
  14. Futures 和 Promises:用于异步编程的机制,允许一个线程等待另一个线程的结果。
  15. 并行模式(Parallel Patterns):包括并行循环、并行迭代器等,用于简化并行计算的模式。
  16. 分布式并行计算框架:如Thrust和Intel TBB等,用于在多核处理器、GPU或分布式计算环境中进行并行计算。
  17. 并发队列(Concurrent Queue):并发队列是一种数据结构,支持多个线程同时入队和出队操作。它通常用于多个生产者和消费者之间的线程安全通信。
  18. 分布式锁(Distributed Lock):用于多个分布式系统节点之间的同步,确保只有一个节点能够访问共享资源。
  19. 交换机制(Barrier,Latch):用于多个线程等待某个事件发生或达到某个状态后再同时继续执行。
  20. 无锁数据结构(Lock-Free Data Structures):这些数据结构设计用于在没有锁的情况下进行并发访问,通常通过原子操作来实现。例如,无锁队列、无锁堆栈等。
  21. 数据流编程(Dataflow Programming):一种并发编程模型,其中数据块在一组并行任务之间流动,任务只有在其输入数据块可用时才会执行。
  22. 软件事务内存(Software Transactional Memory,STM):STM 是一种并发控制机制,允许多个线程同时访问共享数据,通过事务来保护数据的一致性。
  23. 并行算法(Parallel Algorithms):C++标准库提供了一些并行算法,如std::for_eachstd::transform等,用于在多线程环境中高效地处理容器数据。
  24. 线程池(Thread Pool):线程池是一组预先创建的线程,用于执行任务队列中的任务。它可以提高任务的执行效率和资源的复用。
  25. Actor 模型:一种并发编程模型,其中每个 "actor" 是一个独立的执行单元,通过消息传递进行通信和协作。

  1. 内存顺序(Memory Order):在多线程编程中,内存顺序规定了操作在内存中的执行顺序。C++标准库提供了一些原子操作函数,可以用来指定内存顺序,以确保线程之间的操作顺序满足特定需求。
  2. 线程本地存储(Thread-Local Storage,TLS):线程本地存储允许每个线程拥有其自己的变量副本,而不是共享全局变量。这对于避免线程间的竞争条件非常有用。
  3. 任务并行(Task Parallelism):任务并行是一种并发模型,它将工作分解成多个任务,这些任务可以在不同的线程或处理器上并行执行,提高了程序的性能。
  4. 超线程(Hyper-Threading):超线程是一种硬件并发技术,它允许单个物理处理器核心模拟多个逻辑核心,从而提高多线程性能。
  5. 并行计算库:除了C++标准库之外,还有一些第三方并行计算库,如OpenMP、Intel TBB、CUDA等,它们提供了更高级别的并行编程抽象和工具。
  6. 分布式计算:在分布式系统中,线程同步和通信变得更加复杂,需要考虑远程通信和分布式数据访问等问题。一些库和框架,如MPI(消息传递接口)和Apache Hadoop,用于支持分布式计算。
  7. 锁粒度(Lock Granularity):在多线程编程中,锁的粒度是指锁定的范围。选择适当的锁粒度可以影响程序的性能和并发度。