多线程+互斥+条件变量题目

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析

在这里插入图片描述


目录

  • 👉🏻 完成两个线程通过条件变量实现交替打印
    • 错误代码加优化(c++线程库版本)
    • 版本2(使用phtread.h库)
  • 👉🏻按序打印
  • 👉🏻 H2O 生成

👉🏻 完成两个线程通过条件变量实现交替打印

1.题目描述:线程A打印-我是线程A;线程B打印-我是线程B; 最终实现交替打印,不能出现连续的相同打印。

2.本题主要考察条件变量的基本使用流程

错误代码加优化(c++线程库版本)

#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<unistd.h>

using namespace std;
mutex mx;//互斥锁
condition_variable cv;//条件变量

bool flag = true;
void Print(char args)
{
    char c = static_cast<char>(args);
     for (int i = 0; i < 5; i++)
    {
        unique_lock<mutex> lock(mx); // 上锁
        //if(!flag)
        cv.wait(lock,[]{return flag;}); // 等待条件 flag 为 true[]{return flag||!flag;}
        if(c=='A')
        cout << "我是线程A" << endl;
        else if(c=='B')
        cout<<"我是线程B"<<endl;
        flag = !flag;
        cv.notify_one(); // 通知另一个线程
        sleep(1);

    }
}
int main()
{
    //创建AB线程
    std::thread threadA(Print,'A');
    std::thread threadB(Print,'B');

     // 等待线程结束
    threadA.join();
    threadB.join();

    return 0;
}

在这里插入图片描述
此代码会导致第一次打印字符A后就阻塞不动了,
主要原因出在这行代码上

cv.wait(lock,[]{return flag;}); 

wait的第二个参数是个函数对象(这里是lamda表达式),当返回值是false时,wait会进行阻塞,true时,wait会开始释放锁,当前线程拿到锁后开始继续执行。
因为flag一开始为true,所以此时线程A是拿到锁并执行打印工作的。
而后将flag改为false,并去唤醒线程B(唤醒它来看看wait是否满足情况了),此时第二次循环进来,线程A会被wait阻塞住。
而线程B这边已经苏醒,过来第一次循环,结果被阻塞,这是怎么回事?因为flag被改为false了,所以线程B阻塞了,而此时线程A同时也被阻塞,直接导致两个线程统统被阻塞,所以这个wait的判定条件是个大问题。
要解决的话就是将线程A和线程B进来的不同情况都要考虑进去。

代码优化如下:

#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<unistd.h>

using namespace std;
mutex mx;//互斥锁
condition_variable cv;//条件变量

bool flag = true;
void Print(char args)
{
    char c = static_cast<char>(args);
     for (int i = 0; i < 5; i++)
    {
        unique_lock<mutex> lock(mx); // 上锁
        cv.wait(lock,[&]{return c=='A'&&flag||c=='B'&&!flag;}); // 等待条件 flag 为 true[]{return flag||!flag;}
        cout<<"我是线程"<<c<<endl;
        flag = !flag;
        cv.notify_one(); // 通知另一个线程
        sleep(1);

    }
}


int main()
{
    //创建AB线程
    std::thread threadA(Print,'A');
    std::thread threadB(Print,'B');

     // 等待线程结束
    threadA.join();
    threadB.join();

    return 0;
}

在这里插入图片描述

版本2(使用phtread.h库)

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;

//声明互斥锁和条件变量
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;

bool flag = false;
void* Print(void* args)
{
    char c = *(static_cast<char*>(args));

    for(int i = 0;i<5;i++)
    {
        //先上锁为敬
        pthread_mutex_lock(&mtx);
        //判断条件变量
        while(c=='A'&&flag||c=='B'&&!flag)
        {
            pthread_cond_wait(&cv,&mtx);//线程阻塞,等待唤醒
        }
        cout<<"我是线程"<<c<<endl;
        flag = !flag;
        sleep(1);
        pthread_cond_signal(&cv);//唤醒其它线程
        pthread_mutex_unlock(&mtx);//解锁,当其它线程被唤醒时能拿到锁

        
    }
    return nullptr;
}
int main()
{
    //1.线程声明
    pthread_t threadA,threadB;
    char c1 = 'A',c2 = 'B';

    //2.创建线程
    pthread_create(&threadA,nullptr,Print,(void*)&c1);
  
    pthread_create(&threadB,nullptr,Print,(void*)&c2);

    //3.线程等待回收
    pthread_join(threadA,nullptr);
    pthread_join(threadB,nullptr);

    //4.互斥锁和条件变量销毁
    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&cv);

    return 0;
}

这里有几个需要注意的要点:
1.pthread_create中的线程函数返回值必须是void类型的,参数为void,也就是指针类型

2.使用 while 循环则可以避免虚假唤醒的问题。当线程被唤醒时,它会重新检查条件是否满足。如果条件不满足,线程将继续等待,直到条件满足为止。这样可以确保线程在接收到正确的信号时才会继续执行。

👉🏻按序打印

原题链接:按序打印

mycode:


class Foo {
mutex mtx;
condition_variable cv;
int num = 0;
public:
    Foo() {
        
    }

    void first(function<void()> printFirst) {
        
        // printFirst() outputs "first". Do not change or remove this line.
        unique_lock<mutex> lock(mtx);
        cv.wait(lock,[&]{return num==0;});
        printFirst();
        num = (num+1)%3;
        cv.notify_all();//唤醒全部线程
    }

    void second(function<void()> printSecond) {
        
        // printSecond() outputs "second". Do not change or remove this line.
        //上锁
          unique_lock<mutex> lock(mtx);
        cv.wait(lock,[&]{return num==1;});
        printSecond();
        num = (num+1)%3;
        cv.notify_one();//唤醒一个线程
    }

    void third(function<void()> printThird) {
        
        // printThird() outputs "third". Do not change or remove this line.
          unique_lock<mutex> lock(mtx);
        cv.wait(lock,[&]{return num==2;});
        printThird();
        num = (num+1)%3;
        //cv.notify_one();//唤醒一个线程
    }
};

这里要注意的是唤醒线程只需要一个notify_all和notify_one就行了,这样就只会执行3次操作而已,不会过多导致时间超时
为什么呢?因为不管哪个函数先进入,如果因为条件不满足,会堵塞在那里,等待被唤醒,一个notify_all先将其余两个线程唤醒,此时这两个线程过来查看条件变量情况,一个会成功,一个会失败阻塞,这个时候还差一个线程还没执行完全在等待,就再来一个notify_one唤醒即可。

👉🏻 H2O 生成

原题链接:H2O 生成

mycode:

int num_h = 0;
class H2O {
mutex mtx;
condition_variable cv;
public:
    H2O() {
        
    }

    void hydrogen(function<void()> releaseHydrogen) {
        
        // releaseHydrogen() outputs "H". Do not change or remove this line.
         unique_lock<mutex> lock(mtx);
        cv.wait(lock,[&]{return num_h<2;});//H的数量要小于2
        releaseHydrogen();
        ++num_h;
        cv.notify_all();//唤醒氧线程
    }

    void oxygen(function<void()> releaseOxygen) {
        
        // releaseOxygen() outputs "O". Do not change or remove this line.
        unique_lock<mutex> lock(mtx);
        cv.wait(lock,[&]{return num_h==2;});
        num_h = 0;//H与O匹配消耗完后清空
        releaseOxygen();
        cv.notify_all();//唤醒氢气线程
    }
};

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/520135.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

leetcode代码记录(买卖股票的最佳时机 IV

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 给你一个整数数组 prices 和一个整数 k &#xff0c;其中 prices[i] 是某支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说…

【stm32】I2C通信外设

【stm32】I2C通信外设 概念部分 如果简单应用&#xff0c;选择软件I2C。如果对性能指标要求比较高 选择硬件I2C 有硬件电路自动反转引脚电平&#xff0c;软件只需要写入控制寄存器CR和数据寄存器DR 为了实时监控时序的状态&#xff0c;还要读取状态寄存器SR 写入控制寄存器CR…

LC 226.翻转二叉树

226. 翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a; root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a; root [2,1,3] 输出&#xff1a…

【Linux】进程控制之进程程序替换

目录 前言 替换的原理 替换函数 记忆技巧 函数使用 execl execlp execv execvp execle execvpe 调用其它语言的程序 模拟实现一个shell 前言 关于本文可以先去看看上一篇【Linux】进程控制详解-CSDN博客可以更好的理解这里的内容 学完本篇文章&#xff0c;你就…

【Linux操作系统】,Linux运维面试自我介绍

16、右击创建好的虚拟机&#xff0c;打开设置 17、添加自己的CentOS系统文件 18、开启虚拟机 19、使用方向键选择&#xff0c;enter键确定 20、选择中文 21、设置好系统时间以及语言&#xff0c;点击“安装位置” 22、选择自定义分区 23、点击“”&#xff0c;定义分区 24、设置…

jdbc工具类

jdbc 工具类&#xff0c;具体见下面代码&#xff0c;直接可以用。 /*** version 1.0* descpription: jdbc工具类* date 2024/4/6*/ public class JDBCUtils {private static final String URL "jdbc:mysql://127.0.0.1:3306/mybatis";private static final String …

InnoDB中的索引方案

文章目录 InnoDB中的索引方案 InnoDB支持多种类型的索引&#xff0c;包括B-tree索引、全文索引、哈希索引等。B-tree索引是InnoDB存储引擎的默认索引类型&#xff0c;适用于所有的数据类型&#xff0c;包括字符串、数字和日期等。 以下是创建InnoDB表及其B-tree索引的示例代码…

用一个程序解决SQLite常见的各项操作(实用篇)

文章说明&#xff1a; 本篇文章是在之前的一篇文章SQLite3进行数据库各项常用操作基础上写的&#xff0c;将SQLite涉及到的常用的几种操作&#xff0c;以函数的形式处理成相互调用的形式。 因为之前的文章对基础操作已经解释过了&#xff0c;所以这里直接放置可执行代码和结果…

Upload file to cloud server

Procedure is trifle&#xff0c;concentrate on current affair. Via js step1: book a website Official guidebook https://support.huaweicloud.com/usermanual-cloudsite/cloudsite_01_4140.html 流程如下: 为什么这么难&#xff0c;死活不知道怎么备案 /(ㄒoㄒ)/~~

Linux:数据链路层

文章目录 路由表数据链路层分片mac帧报头ARP协议ARP的周边话题 路由表 当主机a想要发送消息到主机b&#xff0c;这一整个过程中&#xff0c;数据报文在进行传输的过程实际上是一跳一跳的过去的&#xff0c;而报文可能会经过公网进行传递&#xff0c;本质上这些网络都是靠对应的…

使用vue开发的前后台框架推荐

对于Vue后台前台框架&#xff0c;以下是几个值得推荐的选项&#xff1a; Element UI&#xff1a;一个基于Vue.js的桌面端组件库&#xff0c;提供了丰富的UI组件和交互方式&#xff0c;非常适合构建后台管理系统。 Element UI是一套为开发者、设计师和产品经理准备的基于Vue 2.…

【计算机毕业设计】五台山景点购票系统,后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

LeetCode-热题100:118. 杨辉三角

题目描述 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows 1 输出: [[1]]…

什么是SYN攻击,有什么办法防御SYN攻击

自进入数字化互联网时代&#xff0c;网络技术给我们带来了许多服务&#xff0c;为人们的生活增添了许多便利。但同时&#xff0c;网络安全问题也日益凸显&#xff0c;其中DDoS攻击&#xff0c;即分布式拒绝服务攻击&#xff0c;已经成为一种常见的网络威胁。这种攻击方式通过控…

anaconda虚拟环境安装apex0.1教程win10

我安装apex0.1的环境是&#xff1a;torch&#xff08;gpu&#xff09;1.8.0&#xff0c;cuda10.2&#xff0c;cuda7.6.5。 第一步&#xff1a;下载对应的pytorch、cuda、cudnn版本 这里就不详细介绍了&#xff0c;具体可以参考我的这篇博文win10中anaconda创建虚拟环境配置py…

「 典型安全漏洞系列 」10.跨域资源共享CORS漏洞详解

跨域资源共享&#xff08;Cross-origin Resource Sharing&#xff0c;CORS&#xff09;是一种浏览器机制&#xff0c;可以对于给定域之外的资源进行受控访问。它扩展并增加了同源政策&#xff08;Same-origin Policy&#xff0c;SOP&#xff09;的灵活性。然而&#xff0c;如果…

知识融合与消歧:完善知识图谱的关键步骤

知识融合与消歧&#xff1a;完善知识图谱的关键步骤 一、引言&#xff1a;知识融合与消歧的重要性 在今天的数据驱动时代&#xff0c;知识图谱已成为组织和理解海量信息的关键技术。它们使得复杂的数据关系可视化&#xff0c;为人工智能提供了丰富的知识基础。然而&#xff0c…

Qt | 元对象系统

一、QByteArray 类简介 1、QByteArray 类简介  该类是一个用于处理字符串的类似于 C++的 string 类型的类,在 Qt 中,对字符串的处理,经常使用的是 QString 类,该类保证字符串以\0结尾,并使用隐式共享(copy-on-write)来减少内存用量和不必要的数据复制。  QByteArra…

【智能算法】原子搜索算法(ASO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2019年&#xff0c;Zhao等人受到原子运动规律启发&#xff0c;提出了原子搜索算法&#xff08;Atom Search Algorithm&#xff0c;ASO&#xff09;。 2.算法原理 2.1算法思想 ASO根据分子动力学中…

Qt学习记录(C++)——Day 2

目录 一、作业 要求&#xff1a; 实现&#xff1a; 1.创建新的窗口类 2. 主窗口中实现 二、 窗口菜单设计 效果展示图 三、图片资源的导入 步骤&#xff1a; 举例&#xff1a; 四、 对话框 1.模拟对话框 2. 非模态对话框 3.错误对话框 4.信息对话框 5.提问对话…