您现在的位置是:首页 >技术交流 >C++ 多线程 手段总结网站首页技术交流
C++ 多线程 手段总结
                简介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;
}
                风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。
        
    
        
    
            




U8W/U8W-Mini使用与常见问题解决
QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结