多线程编程:线程间的同步与通讯

打印 上一主题 下一主题

主题 821|帖子 821|积分 2463

多线程编程:线程间的同步与通讯


一、概述

在多线程编程中,必须解决以下两个核心问题:

  • 线程间的互斥:确保共享资源在同一时刻只被一个线程访问,防止数据竞争。
  • 线程间的同步通讯:使线程之间按照指定序次执行,以实现任务协调和依赖关系。

二、线程间的互斥

1. 静态条件与临界区



  • 静态条件:多线程步伐在差异执行序次下可能产生不一致的结果。
  • 临界区:代码中访问共享资源的部分。

    • 解决方法:每次只能允许一个线程进入临界区。

2. 互斥锁(Mutex)



  • 作用

    • 包管临界区代码段的原子性。
    • 防止多个线程同时访问共享资源。

  • 实现方式

    • 使用 std::mutex 加锁和解锁。
    • 对于较小的临界区,可以使用轻量级的无锁机制(如 CAS)。


三、线程间的同步通讯

1. 同步通讯的必要性



  • 多线程步伐中,线程的执行序次由操纵系统调理决定,通常是不确定的。
  • 在某些场景下,线程必要依赖其他线程的执行结果。例如:

    • 生产者-消费者问题:生产者必要将数据生产完毕后关照消费者举行消费。

2. 条件变量(Condition Variable)



  • 作用:实现线程间的同步通讯。
  • 特点

    • 必要与 std::mutex 搭配使用。
    • 提供 wait 和 notify 方法,实现线程间的协调。


四、生产者-消费者模子

1. 问题描述

生产者线程负责生产数据,并将数据放入队列;消费者线程负责从队列中取出数据并消费。


  • 目标

    • 包管生产者和消费者交替工作。
    • 防止队列为空时消费者消费数据或队列溢出时生产者继续生产。


2. 代码实现

全局变量

  1. std::queue<int> q;                  // 共享队列
  2. std::mutex mtx;                     // 互斥锁
  3. std::condition_variable cv;         // 条件变量
复制代码
生产者函数

  1. void producer() {
  2.     for (int i = 1; i <= 10; ++i) {
  3.         std::unique_lock<std::mutex> lock(mtx); // 加锁
  4.         q.push(i);                              // 生产数据
  5.         std::cout << "Produced: " << i << std::endl;
  6.         cv.notify_all();                        // 通知消费者
  7.         std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟生产时间
  8.     }
  9. }
复制代码
消费者函数

  1. void consumer() {
  2.     for (int i = 1; i <= 10; ++i) {
  3.         std::unique_lock<std::mutex> lock(mtx);               // 加锁
  4.         cv.wait(lock, [] { return !q.empty(); });             // 等待队列非空
  5.         int value = q.front();                                // 获取数据
  6.         q.pop();                                              // 移除数据
  7.         std::cout << "Consumed: " << value << std::endl;      // 打印消费日志
  8.         lock.unlock();                                        // 解锁
  9.         cv.notify_all();                                      // 通知生产者
  10.         std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟消费时间
  11.     }
  12. }
复制代码
主函数

  1. int main() {
  2.     std::thread t1(producer); // 创建生产者线程
  3.     std::thread t2(consumer); // 创建消费者线程
  4.     t1.join();                // 等待生产者线程完成
  5.     t2.join();                // 等待消费者线程完成
  6.     return 0;
  7. }
复制代码

3. 核心逻辑

同步通讯的实现


  • 生产者和消费者的协调

    • 生产者生产数据后通过 cv.notify_all() 关照消费者。
    • 消费者消费数据后通过 cv.notify_all() 关照生产者。

  • 防止竞争和死锁

    • 使用 std::mutex 确保队列的操纵线程安全。
    • cv.wait 确保消费者只有在队列非空时才执行操纵。


4. 状态厘革分析

变乱状态举动消费者发现队列为空阻塞等候生产者生产数据后被关照生产者生产数据队列非空,关照消费者消费者从等候状态转为阻塞状态,等候获取锁继续执行消费者消费数据队列可能为空,关照生产者生产者从等候状态转为阻塞状态,等候获取锁继续执行
6. 注意事项


  • 线程安全

    • 队列的全部操纵(如 push 和 pop)必须在加锁状态下举行。

  • 条件变量使用

    • wait 必须传入一个 std::unique_lock<std::mutex> 对象。
    • notify_all 应用于多个消费者时,notify_one 适用于单一消费者场景。

  • 死锁防范

    • 确保每个线程在进入 wait 状态前释放锁。


五、总结


  • 互斥锁的作用

    • 确保线程安全,制止共享资源的并发访问导致的数据竞争。

  • 条件变量的作用

    • 线程间的同步通讯,协调线程的执行序次。

  • 核心操纵

    • std::mutex:加锁与解锁。
    • std::condition_variable:线程等候(wait)与关照(notify_all 和 notify_one)。

  • 生产者-消费者模子的实现

    • 使用共享队列举行数据通报。
    • 条件变量协调线程之间的依赖序次。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

伤心客

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表