当前位置: 首页 > news >正文

C++多线程编程—线程控制、同步与互斥详解

本文将深入探讨C++多线程编程中的核心概念:线程控制、同步与互斥

1.线程控制:join 与 detach

  当我们创建一个线程(std::thread)后,我们必须明确在这个线程对象销毁之前,如何管理它所代表的执行线程。这就是 join 和 detach 的用武之地。

 

join()

  • 作用:阻塞当前线程(通常是主线程),等待被 join 的线程执行完毕后,再继续执行当前线程。
  • 含义:“等待这个线程工作完成。”
  • 注意:一个线程只能被 join 一次。

 

detach()

  • 作用:将被 detach 的线程与 std::thread 对象分离,让该线程在后台独立运行。一旦分离,原 std::thread 对象不再代表任何线程,也无法再对它进行控制。
  • 含义:“这个线程可以自己去流浪了,我不再管它了。”
  • 注意:分离后的线程通常称为守护线程。确保线程中访问的数据在其生命周期内有效是程序员的责任。主线程退出时,所有分离的线程也会被强制终止。
#include <iostream>
#include <thread>
#include <chrono>void worker() {std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "子线程工作完成...\n";
}int main() {std::thread t(worker);// 主线程在此阻塞,等待 t 执行完毕// t.join();// 或者,将 t 分离,让其后台运行
    t.detach();// 注意:detach 后,主线程不会等待 workerstd::cout << "主线程继续执行...\n";std::this_thread::sleep_for(std::chrono::seconds(2)); // 防止主线程过早结束杀死detach的线程// 错误!线程分离或join后,不能再join// if (t.joinable()) t.join(); return 0;
}

输出(使用 join):

子线程工作完成...
主线程继续执行...

输出(使用 detach):

主线程继续执行...
子线程工作完成... 

2. 线程的互斥:std::mutex
  当多个线程需要访问共享数据时,如果不加控制,会导致数据竞争,引发未定义行为。std::mutex(互斥锁)是最基本的同步原语,用于保护共享数据,确保一次只有一个线程可以访问。

lock() 与 unlock()

  • lock():尝试获取锁。如果锁已被其他线程持有,则当前线程被阻塞,直到获取到锁。
  • unlock():释放锁,允许其他被阻塞的线程获取它。

注意:必须成对使用 lock() 和 unlock(),否则会导致死锁。为了避免忘记 unlock(),推荐使用RAII机制的包装类,如 std::lock_guard。

#include <iostream>
#include <thread>
#include <mutex>std::mutex g_mutex;
int shared_data = 0;void increment() {for (int i = 0; i < 100000; ++i) {g_mutex.lock();   // 获取锁++shared_data;    // 临界区操作g_mutex.unlock(); // 释放锁
    }
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "最终结果: " << shared_data << std::endl; // 总是 200000return 0;
}

3. RAII锁管理:std::lock_guard 和 std::unique_lock
  手动管理 lock 和 unlock 非常容易出错。C++提供了RAII风格的锁管理类,在构造时加锁,析构时自动解锁,极大地提高了代码的安全性和简洁性。

std::lock_guard

  • 特点:简单、轻量级、不可复制。它在构造时锁定互斥量,在析构时自动解锁。它没有提供手动加锁或解锁的接口。
  • 适用场景:简单的临界区,整个作用域都需要加锁。
void safe_increment_lock_guard() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(g_mutex); // 构造时加锁,析构时解锁++shared_data;}
}

std::unique_lock

  • 特点:比 std::lock_guard 更灵活但稍重。它支持延迟锁定、手动锁定、解锁、条件变量等。
  • 适用场景:需要更灵活锁管理的场景,例如需要提前解锁或与条件变量配合使用。
void safe_increment_unique_lock() {for (int i = 0; i < 100000; ++i) {std::unique_lock<std::mutex> lock(g_mutex); // 同样,构造时加锁++shared_data;lock.unlock(); // 可以提前解锁,减少锁的持有时间// ... 执行一些不共享的操作// 不需要再手动lock,因为析构函数会检查状态,如果已解锁则什么都不做
    }
}

4. 线程的同步:std::condition_variable
  互斥锁解决了数据竞争问题,但线程间有时需要协同工作。例如,一个线程需要等待另一个线程完成任务或满足某个条件后再继续执行。这就是条件变量 std::condition_variable 的作用。它通常与 std::mutex(尤其是 std::unique_lock)配合使用。

核心操作:
wait(lock, predicate):

  1. 原子地解锁 lock 并阻塞当前线程。
  2. 当被 notify_one() 或 notify_all() 唤醒时,线程会重新获取锁并检查 predicate(可调用对象,返回bool)。
  3. 如果 predicate 返回 true,则 wait 返回,线程继续执行。
  4. 如果 predicate 返回 false,则线程再次解锁并阻塞(predicate 参数可省略,但建议使用以避免虚假唤醒)
  • notify_one():随机唤醒一个正在等待此条件变量的线程。
  • notify_all():唤醒所有正在等待此条件变量的线程。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
const int MAX_ITEMS = 10;void producer() {for (int i = 0; i < MAX_ITEMS; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(100));std::unique_lock<std::mutex> lock(mtx);// 如果队列已满,生产者等待消费者消费cv.wait(lock, [] { return data_queue.size() < MAX_ITEMS; });data_queue.push(i);std::cout << "生产: " << i << std::endl;lock.unlock(); // 提前解锁cv.notify_one(); // 通知一个消费者
    }
}void consumer() {while (true) {std::unique_lock<std::mutex> lock(mtx);// 如果队列为空,消费者等待生产者生产cv.wait(lock, [] { return !data_queue.empty(); });int data = data_queue.front();data_queue.pop();std::cout << "消费: " << data << std::endl;if (data == MAX_ITEMS - 1) break; // 简单退出条件lock.unlock();cv.notify_one(); // 通知一个生产者
    }
}int main() {std::thread p(producer);std::thread c(consumer);p.join();c.join();return 0;
}
http://www.wxhsa.cn/company.asp?id=5467

相关文章:

  • MySQL启动失败:mysqld.log Permis 报错处理.250916
  • 源码管理—密钥硬编码问题
  • 无速度传感器交流电机的扩展Luenberger观测器
  • AI Ping体验记:终于有人做大模型服务的“性能监控”了
  • 数据库原理-第二章——关系型数据库
  • mac 的任务栏 Windows-Style Taskbar For macOS
  • 快手Java一面
  • 详细介绍:Elastic APM 入门指南:快速设置应用性能监控
  • 想找Axure替代?这6个原型设计工具值得一试
  • H5游戏性能优化系列-----cpu相关优化
  • IPA 混淆实战 IPA 混淆、IPA 加固、ipa 文件安全与成品包防护全流程指南
  • 实用指南:javaweb HTML基本介绍/常见标签
  • 文档处理控件Aspose.Words教程:在 C# 中将 Markdown 转换为 PDF
  • TCP协议与wireshark
  • docker容器mysql导入sql文件
  • ObjectSense 包与模块:代码组织的艺术
  • IDE工具RAD Studio 13 Florence重磅发布:64 位 IDE + AI 组件全面升级!
  • C# 批量修改数据库
  • Job for network.service failed because the control process exited with error code.
  • 负荷聚类及其在MATLAB中的实现
  • 移动安全框架(MobSF)静态分析入门指南
  • 列表项点击,逻辑梳理
  • CRMEB标准版PHP批量发货功能深度解析
  • 数学之美 第一章读后感
  • 【SPIE出版】第九届交通工程与运输系统国际学术会议(ICTETS 2025)
  • 模型上下文协议(Model Context Protocol,MCP)
  • 大华设备视频平台EasyCVR视频分析设备平台双轨视频数据存储方案全解读
  • AI音乐创作新突破:ACE-Step模型开启放克音乐智能生成时代
  • 【ABSR出版】第二届农业工程与生物学国际研讨会(ISAEB 2025)
  • 符号执行技术实践-求解程序密码