C++多线程示例

示例

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

using namespace std;

queue<int> q; //共享队列,给生产者和消费者同时操作
bool is_done;  //共享变量:判断是否完成
mutex mtx;    //互斥量:保护共享队列
condition_variable cv; //条件变量:同步用


//生产者
void producer() {
    for (int i = 1; i <= 20; ++i) {
        this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟生产时间:生产比消费快
        unique_lock<std::mutex> lock(mtx); // 加锁:如果不加锁,q可能被消费者,然后这里再生产破坏q
        q.push(i+100);                              // 生产一个任务
        lock.unlock();                          // 解锁
        cv.notify_one();                        // 通知其中一个消费者,具体哪个消费者由调度器决定
    }
    //全部生产完之后,设置标志完成,并通知所有消费者
    unique_lock<std::mutex> lock(mtx);
    is_done = true;                              
    lock.unlock();
    cv.notify_all();                            // 通知所有消费者
}

//消费者:5个
void consumer(int id) {
    while (true) {
        unique_lock<mutex> lock(mtx);
        cv.wait(lock, []{ return !q.empty() || is_done; }); // 等待队列非空或生产者完成
        if (is_done && q.empty())       // 如果队列为空且生产者完成,退出
            break;                    
        int item = q.front();                            // 消费一个任务
        q.pop();
        lock.unlock();
        cout << "Consumer " << id << " consumed item " << item << endl;
        this_thread::sleep_for(chrono::milliseconds(50)); // 模拟消费时间
    }
}

int main() {
    const int num_consumers = 3; // 消费者数量
    thread producer_thread(producer); // 创建生产者线程
    thread consumer_threads[num_consumers]; // 创建消费者线程数组

    // 启动消费者线程
    for (int i = 0; i < num_consumers; ++i) {
        consumer_threads[i] = thread(consumer, i + 1);
    }

    // 等待生产者线程结束
    producer_thread.join();

    // 等待所有消费者线程结束
    for (auto& th : consumer_threads) {
        th.join();
    }

    cout << "All threads finished." << endl;
    return 0;
}

producer() 函数
std::unique_lockstd::mutex lock(mtx);
• 进入临界区,把队列锁住。
• 相比 lock_guard,unique_lock 可以在后面 手动 unlock(),因此更灵活。

lock.unlock();
• 提前解锁的好处:
– 减少锁的粒度,让消费者尽早拿到锁。
– 避免在 notify_one() 时仍然持有锁(某些实现下会导致唤醒线程立即再次阻塞)。

consumer() 函数
cv.wait(lock, predicate)
• wait 会原子地释放 lock 并挂起线程;
• 被唤醒后先重新拿到锁,再检查谓词:
[]{ return !q.empty() || done; }
只要“队列里有东西”或“生产者已经结束”就继续,否则继续睡。
• 这是 防止虚假唤醒 (spurious wakeup) 的标准写法。
while (!q.empty()) { … }
• 生产者可能一次放入多个任务,或通知时队列里已有多条数据,所以用 内层 while 一次性处理干净。
• 注意:仍要在临界区内操作队列。

行退出条件
• 必须同时满足:done == true 且 q.empty(),确保所有数据都被消费完。

常见疑问 & 易错点

为什么用 unique_lock 而不是 lock_guard?
因为 lock_guard 不能中途 unlock(),而 unique_lock 可以,所以更适合配合条件变量。
如果忘记 notify_one() 会怎样?
消费者可能永远阻塞在 cv.wait(),程序卡死。
如果把 done 设成 atomic<bool> 不加锁行不行?
不行。虽然 done 本身是原子,但队列状态仍要互斥,否则会出现“看到 done=true 但队列却非空” 的竞态。
为什么消费者里还要再检查一次 q.empty()?
因为唤醒后可能有多个线程同时被唤醒(如果使用 notify_all()),或者生产者一次 push 了多条数据。

一句话总结
条件变量 + 互斥量 = 线程安全的“等待-通知”机制;
核心套路:
• 修改共享数据 → 加锁
• 修改完 → 先解锁再 notify_*
• 等待方 → while(!predicate) cv.wait(…)

相关函数

  1. 线程管理
类/函数功能
std::thread创建并管理线程
std::thread::join()等待线程结束
std::thread::detach()分离线程,使其独立运行
std::thread::get_id()获取线程 ID
std::thread::hardware_concurrency()获取系统支持的并发线程数
  1. 互斥量
类/函数功能
std::mutex普通互斥量
std::recursive_mutex递归互斥量(可多次加锁)
std::timed_mutex带超时功能的互斥量
std::shared_mutex (C++17)共享互斥量(支持多个读锁)
  1. 锁管理
类/函数功能
std::lock_guard自动加锁/解锁(RAII)
std::unique_lock可手动加锁/解锁的锁
std::scoped_lock (C++17)一次性加多个锁,避免死锁
  1. 条件变量
类/函数功能
std::condition_variable条件变量(用于线程同步)
std::condition_variable::wait()等待条件满足
std::condition_variable::notify_one()通知一个等待的线程
std::condition_variable::notify_all()通知所有等待的线程
  1. 异步任务
类/函数功能
std::async()启动异步任务
std::future获取异步任务的结果
std::future::get()获取异步任务的返回值
std::future::wait()等待异步任务完成
std::promise设置异步任务的返回值
std::promise::set_value()设置异步任务的结果
std::promise::get_future()获取与承诺关联的 std::future
  1. 原子操作
类/函数功能
std::atomic原子变量
std::atomic::fetch_add()原子加法
std::atomic::fetch_sub()原子减法
std::atomic::load()加载当前值
std::atomic::store()存储新值
  1. 线程工具
类/函数功能
std::thread_local声明线程局部变量
std::this_thread::get_id()获取当前线程的 ID
std::this_thread::sleep_for()让当前线程休眠一段时间
std::this_thread::yield()让出当前线程的执行权
以下是一个简单的C++多线程示例,使用C++11标准中的`<thread>`库实现。示例包含线程创建、同步(互斥锁)和基本线程操作: --- ### **示例代码:多线程计算与同步** ```cpp #include <iostream> #include <thread> #include <vector> #include <mutex> std::mutex mtx; // 互斥锁,用于同步输出 // 线程函数:打印数字 void printNumbers(int thread_id, int start, int end) { for (int i = start; i <= end; ++i) { std::lock_guard<std::mutex> lock(mtx); // 加锁,避免输出混乱 std::cout << "Thread " << thread_id << ": " << i << std::endl; } } // 线程函数:计算向量元素的和 void sumVector(const std::vector<int>& vec, int& result) { int sum = 0; for (int num : vec) { sum += num; } std::lock_guard<std::mutex> lock(mtx); // 加锁,保护共享变量 result = sum; std::cout << "Sub-sum calculated: " << sum << std::endl; } int main() { // 示例1:启动多个线程打印数字 std::thread t1(printNumbers, 1, 1, 5); std::thread t2(printNumbers, 2, 6, 10); t1.join(); // 等待线程t1结束 t2.join(); // 等待线程t2结束 // 示例2:多线程计算向量总和 std::vector<int> numbers = {1, 2, 3, 4, 5}; int total_sum = 0; // 分段计算(模拟并行) std::vector<std::thread> workers; int partial_result; std::thread worker1(sumVector, std::ref(numbers), std::ref(partial_result)); worker1.join(); // 合并结果(此处简化,实际可能需要更复杂的同步) total_sum += partial_result; std::cout << "Total sum: " << total_sum << std::endl; return 0; } ``` --- ### **关键点说明** 1. **线程创建** - 使用`std::thread`构造函数,传入函数名和参数。 - 线程函数可以是普通函数、Lambda表达式或成员函数(需绑定对象)。 2. **线程同步** - `std::mutex` + `std::lock_guard`:确保对共享资源(如`cout`或变量)的互斥访问。 - 避免竞态条件(如多个线程同时修改`result`)。 3. **线程管理** - `join()`:等待线程结束,防止主线程提前退出。 - `detach()`:让线程在后台运行(需谨慎使用,避免悬垂线程)。 4. **参数传递** - 使用`std::ref`传递引用(如`result`),否则会复制值。 --- ### **扩展:使用Lambda表达式** ```cpp #include <thread> #include <iostream> int main() { auto task = [](int id) { std::cout << "Lambda Thread " << id << " is running.\n"; }; std::thread t(task, 1); t.join(); return 0; } ``` --- ### **常见问题** 1. **线程安全** - 多个线程操作同一变量时,必须同步(如用`mutex`或原子操作`std::atomic`)。 2. **死锁风险** - 避免嵌套锁或未释放锁(推荐使用`lock_guard`自动管理锁生命周期)。 3. **性能考虑** - 线程创建有开销,频繁创建销毁线程时建议用线程池(如`std::async`或第三方库)。 --- ### **编译命令** 确保编译器支持C++11(如g++添加`-std=c++11`): ```bash g++ -std=c++11 -pthread thread_example.cpp -o thread_example ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Charles Ray

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

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

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

打赏作者

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

抵扣说明:

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

余额充值