您现在的位置是:首页 >其他 >C++ 多线程 手段总结网站首页其他

C++ 多线程 手段总结

darkchink 2025-07-11 00:01:03
简介C++ 多线程 手段总结

C++ 多线程手段详解

1. std::mutex

使用场景

std::mutex 用于保护共享资源,确保在同一时刻只有一个线程可以访问该资源。常见的使用场景包括:

  • 保护全局变量
  • 保护文件读写操作
  • 保护数据库连接

实现原理

std::mutex 的实现依赖于操作系统的同步原语。在 Windows 上,它调用 CRITICAL_SECTION,而在 Linux 上,它调用 pthread_mutex_t

例子

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

std::mutex mtx;  // 互斥锁

void printThread(int id) {
    std::lock_guard<std::mutex> lock(mtx);  // 自动管理锁
    std::cout << "Thread ID: " << id << std::endl;
}

int main() {
    std::thread t1(printThread, 1);
    std::thread t2(printThread, 2);
    t1.join();
    t2.join();
    return 0;
}

2. std::condition_variable

使用场景

std::condition_variable 用于线程间的通信和协调,特别是在生产者-消费者模式中非常有用。

实现原理

std::condition_variable 本质上是比 std::mutex 多了阻塞当前线程的功能,例如等待和唤醒操作。具体实现如下:

  • wait 方法
    • 释放锁:调用 wait 方法时,首先会释放当前持有的锁(通常是 std::unique_lock)。这是通过调用 std::unique_lock 的 unlock 方法实现的。
    • 进入等待状态:然后,线程会进入等待状态,释放 CPU 资源。这是通过调用操作系统级别的条件变量的等待函数实现的,例如 pthread_cond_wait 或 WaitForMultipleObjects
    • 重新获取锁:当线程被唤醒时,它会重新尝试获取之前释放的锁。这是通过调用 std::unique_lock 的构造函数或 lock 方法实现的。一旦获取成功,才会从 wait 方法返回。
  • notify_one 方法
    • 唤醒一个线程:调用 notify_one 方法时,会选择一个等待在条件变量上的线程并将其唤醒。具体选择哪个线程是由操作系统调度器决定的。这是通过调用操作系统级别的条件变量的通知函数实现的,例如 pthread_cond_signal 或 PulseEvent
  • notify_all 方法
    • 唤醒所有线程:调用 notify_all 方法时,会唤醒所有等待在条件变量上的线程。这些线程都会尝试重新获取锁,但只有一个线程能成功获取锁并继续执行,其他线程会再次进入等待状态。这是通过调用操作系统级别的条件变量的通知函数实现的,例如 pthread_cond_broadcast 或 PulseEvent

例子

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

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

void waitFunction() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });  // 等待条件满足
    std::cout << "Condition met, continuing..." << std::endl;
}

void notifyFunction() {
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟延迟
    std::lock_guard<std::mutex> lock(mtx);
    ready = true;
    cv.notify_one();  // 通知等待的线程
}

int main() {
    std::thread t1(waitFunction);
    std::thread t2(notifyFunction);
    t1.join();
    t2.join();
    return 0;
}

3. std::atomic

使用场景

std::atomic 主要用于简单资源的同步,适用于以下场景:

  • 计数器:确保计数操作是原子的。
  • 标志位:确保标志位的读写操作是原子的。
  • 无锁数据结构:实现高性能的无锁数据结构。
  • 并发计数器:确保多个计数器的读写操作是原子的。
  • 状态同步:确保状态的读写操作是原子的,避免竞态条件。

实现原理

std::atomic 的实现基于硬件支持和内存模型:

  • 硬件支持std::atomic 会被编译器编译成原子化操作的 CPU 指令。
  • 内存模型:能够使用不同的内存模型,提供更精细化的可见性。常见的内存顺序选项包括:
    • std::memory_order_relaxed:最弱的内存顺序,只保证操作本身是原子的,不提供任何额外的内存顺序保证。
    • std::memory_order_consume:保证消费操作之前的读取操作在消费操作之后可见。
    • std::memory_order_acquire:保证读取操作之前的读取操作在读取操作之后可见。
    • std::memory_order_release:保证写入操作之前的写入操作在写入操作之后可见。
    • std::memory_order_acq_rel:同时具有 acquire 和 release 的效果。
    • std::memory_order_seq_cst:最强的内存顺序,保证所有线程看到的操作顺序是一致的。

例子

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

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

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

int main() {
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);
    t1.join();
    t2.join();
    std::cout << "Final counter value: " << counter.load() << std::endl;
    return 0;
}

4. std::async 和 std::future

使用场景

std::async 和 std::future 适用于以下场景:

  • 异步计算:需要异步执行计算任务的场景。
  • 异步 I/O:需要非阻塞 I/O 的场景。
  • 异步任务链:需要按顺序执行多个异步任务的场景。

实现原理

std::async 和 std::future 基于 SharedState 共享状态来实现,主要通过 std::mutex 和 std::condition_variable 进行同步。

例子

#include <iostream>
#include <future>
#include <thread>

int computeValue(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟延迟
    return x * x;
}

int main() {
    std::future<int> result = std::async(std::launch::async, computeValue, 5);
    std::cout << "Doing some other work..." << std::endl;
    int value = result.get();  // 获取异步任务的结果
    std::cout << "Result: " << value << std::endl;
    return 0;
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。