重入的问题搞清楚

news2024/11/17 23:28:12

很久很久之前,写入重入问题的文章

如果你在笔试的实际,面试官问
——下面这个代码有什么问题?

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

int NUM = 0;

void * PrintNum(void * ptr)
{
    while(NUM++ < 200000000) {
    }
    printf("%s,NUM:%d\n", (char*)ptr, NUM);
    NUM = 0;
}

int main(int argc,char ** argv)
{
    pthread_t thread1 = -1;
    char thread1_name[] = "thread2";
    if(pthread_create(&thread1, NULL, PrintNum, thread1_name)!=0) {
        printf("thread1 creat error\n");
    }

    pthread_t thread2 = -1;
    char thread2_name[] = "thread2";
    if(pthread_create(&thread1, NULL, PrintNum, thread2_name)!=0) {
        printf("thread1 creat error\n");
    }

    void * result1;
    void * result2;
    pthread_join(thread1, &result1);
    pthread_join(thread2, &result2);
}

这个代码有以下问题:

在创建第二个线程时,使用了错误的变量名。

应该将 pthread_create(&thread1, NULL, PrintNum, thread2_name) 
改为 pthread_create(&
thread2, NULL, PrintNum, thread2_name)。

在 PrintNum 函数中,没有使用同步机制来保证对全局变量 NUM 的访问是线程安全的。多个线程同时访问 NUM 可能会导致数据不一致或者程序出现错误。应该使用互斥锁等同步机制来保证线程安全。

在 PrintNum 函数中,使用了一个死循环来模拟线程执行的时间。这样会导致线程一直占用 CPU 资源,影响系统的性能。应该使用 sleep 函数等待一段时间,让线程进入阻塞状态,释放 CPU 资源。

在 main 函数中,没有检查线程创建和等待的返回值,线程创建和等待可能会失败,应该检查返回值并进行错误处理。

在 main 函数中,没有释放线程资源。
应该在线程执行完毕后调用 pthread_exit 函数或者 pthread_cancel 函数来释放线程资源。

在 main 函数中,没有使用 return 语句来结束程序,应该在程序执行完毕后使用 return 语句来结束程序。

上面的答案是我把代码扔给chatgpt得到的回复。

综上所述,这个代码需要进行一些修改才能正确地运行。

修改下代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

unsigned long int NUM = 0;

void * PrintNum(void * ptr)
{
    unsigned long int count = 0;
    while(NUM++ < 20000) {
      count++;
    }
    printf("%s run %lu in NUM:%lu \n", (char*)ptr, count, NUM);
    NUM = 0;
    pthread_exit(NULL);
}

int main(int argc,char ** argv)
{
    pthread_t thread1 = -1;
    char thread1_name[] = "thread2";
    if(pthread_create(&thread1, NULL, PrintNum, thread1_name)!=0) {
        printf("thread1 creat error\n");
    }

    pthread_t thread2 = -1;
    char thread2_name[] = "thread2";
    if(pthread_create(&thread2, NULL, PrintNum, thread2_name)!=0) {
        printf("thread1 creat error\n");
    }

    void * result1;
    void * result2;
    pthread_join(thread1, &result1);
    pthread_join(thread2, &result2);

    return 0;
}

执行函数输出

thread2 .....run 18188 in NUM:20001 
thread2 .....run 6812 in NUM:20001

发现在计算里面,两个线程对全局变量的执行次数是不一样的。

然后我就进行了各种捣鼓

直到最后,才完成了我最初的想法

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>

unsigned long int g_num = 0;
unsigned long int count_thread1 = 0, count_thread2 = 0;
#define RUN_COUNT 2000
pthread_mutex_t mutex;
pthread_cond_t cond;
int cond_flag = 1;

void * print_num(void * ptr)
{
    while(1) {
        pthread_mutex_lock(&mutex);
        if (g_num >= RUN_COUNT) {
            pthread_mutex_unlock(&mutex);
            break;
        }
        if (strcmp((char*)ptr, "thread1") == 0) {
            if (cond_flag != 1) {
              pthread_cond_wait(&cond, &mutex);
            }
        } else if (strcmp((char*)ptr, "thread2") == 0) {
            if (cond_flag != 2) {
              pthread_cond_wait(&cond, &mutex);
            }
        }
        if (strcmp((char*)ptr, "thread1") == 0) {
            count_thread1++;
            cond_flag = 2;
        } else if (strcmp((char*)ptr, "thread2") == 0) {
            count_thread2++;
            cond_flag = 1;
        }
        g_num++;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
    if (strcmp((char*)ptr, "thread1") == 0) {
        printf("%s run %lu in g_num:%lu \n", (char*)ptr, count_thread1, g_num);
    } else if (strcmp((char*)ptr, "thread2") == 0) {
        printf("%s run %lu in g_num:%lu \n", (char*)ptr, count_thread2, g_num);
    }
    g_num = 0;
    printf("%s run over \n", (char*)ptr);
    pthread_exit(NULL);
}
int main(int argc,char ** argv)
{
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_t thread1 = -1;
    char thread1_name[] = "thread1";
    if(pthread_create(&thread1, NULL, print_num, thread1_name)!=0) {
        printf("thread1 creat errorn");
        return -1;
    }
    pthread_t thread2 = -1;
    char thread2_name[] = "thread2";
    if(pthread_create(&thread2, NULL, print_num, thread2_name)!=0) {
        printf("thread1 creat errorn");
        return -1;
    }
    while (1) {
        if (g_num > RUN_COUNT) {
          if (count_thread1 < RUN_COUNT/2) {
              cond_flag = 1;
              pthread_cond_signal(&cond);
          } else if (count_thread2 < RUN_COUNT/2) {
              cond_flag = 2;
              pthread_cond_signal(&cond);
          } else {
            break;
          }
        }
    }
    void * result1;
    void * result2;
    pthread_join(thread1, &result1);
    pthread_join(thread2, &result2);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

代码输出

thread2 run 1000 in g_num:2000 
thread2 run over 
thread1 run 1001 in g_num:2001 
thread1 run over

上面的两段代码,第一段对临界区没有做任何保护,两个线程在调度的时候,对临界区的访问也是随机的。

第二段加上了条件变量和锁保护,让整个过程进入规则性运行,从而保证了软件的逻辑。

如果只增加锁保护而不增加条件变量的话,那只能保证临界区一个时间片内有一个线程访问,但是并不能保证对临界区访问的顺序性。

C++中的条件变量使用,大家可以看看这些链接

https://en.cppreference.com/w/cpp/thread/condition_variable

https://stackoverflow.com/questions/73543664/do-i-need-to-lock-the-mutex-before-calling-condition-variablenotify

https://www.zhihu.com/question/541037047

44b29ca36fcdbb203e848fdb96920777.png

0e80c7eeeb95a863d53fd798ade7350b.jpeg

7348a3d69ee5081a0df3dcc47b57e28b.jpeg

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录算法训练营第三十四天|1005.K次取反后最大化的数组和、134. 加油站、135. 分发糖果

文章目录 1005.K次取反后最大化的数组和134. 加油站:star:135. 分发糖果:star::star::star: 1005.K次取反后最大化的数组和 链接:代码随想录解题思路&#xff1a; 遇到负数就去反,k– 当k能遇到正数的是否&#xff0c;如果k % 2 1 必须取一个最小的正数进行取反再加才行 pub…

Vue 计算属性

文章目录 Vue 计算属性computed vs methodscomputed setter Vue 计算属性 计算属性关键词: computed。 计算属性在处理一些复杂逻辑时是很有用的。 可以看下以下反转字符串的例子&#xff1a; 实例 1 <div id"app">{{ message.split().reverse().join() }}…

【SpringBoot2】SpringBoot开发实用篇

SpringBoot开发实用篇 KF-1.热部署 ​ 什么是热部署&#xff1f;简单说就是你程序改了&#xff0c;现在要重新启动服务器&#xff0c;嫌麻烦&#xff1f;不用重启&#xff0c;服务器会自己悄悄的把更新后的程序给重新加载一遍&#xff0c;这就是热部署。 ​ 热部署的功能是如…

使用CDN的好处

基于移动和应用程序的互联网迫使越来越多的内容提供商、内容出版商和在线供应商简化导航并改善用户体验&#xff0c;主要是网站的页面加载时间。 以下是您必须考虑在业务生态系统中实施CDN的8个原因&#xff1a; 全球可访问性&#xff1a;内容交付网络有助于使内容在全球范围内…

图像分类识别(方向/重点指引)

1、继YOLO之后的高效目标检测算法&#xff1a; CenterNet 继YOLO之后的高效目标检测算法&#xff1a; CenterNet 2、百度飞浆面向 AI 行业应用场景的开源项目&#xff1a;GitHub - PaddlePaddle/PaddleX: PaddlePaddle End-to-End Development Toolkit&#xff08;『飞桨』…

在win10系统中安装anaconda

1、 Anaconda的下载 你可以根据你的操作系统是32位还是64位选择对应的版本到官网下载&#xff0c;但是官网下载龟速。 建议到清华大学镜像站下载 &#xff1a;Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source MirrorIndex of /anaconda/archiv…

1.微服务项目实战---SpringCloud Alibaba

1.1 系统架构演变 随着互联网的发展&#xff0c;网站应用的规模也在不断的扩大&#xff0c;进而导致系统架构也在不断的进行变化。 从互联网早起到现在&#xff0c;系统架构大体经历了下面几个过程 : 单体应用架构 ---> 垂直应用架构 ---> 分布 式架构--->SOA 架构…

【学习总结】openvins中的IMU数据仿真

本文介绍 openvins 中IMU仿真时基于控制轨迹和SPline插值&#xff0c;并计算IMU输出&#xff0c;的原理和代码。 参考 Open-vins中关于仿真的描述&#xff1a;https://docs.openvins.com/simulation.html Open-vins论文&#xff1a;https://pgeneva.com/downloads/papers/Gen…

蜂鸟空间数据引擎助力设施管理解决方案

随着产业价值分解和专业化发展&#xff0c;资产和物业管理领域出现了精深精细化的趋势&#xff0c;并逐渐从劳动密集型向知识密集型进行转变&#xff0c;在此基础上&#xff0c;资产和物业管理产生了一个新型的细分领域——设施管理&#xff08;Facility Management&#xff0c…

typescript中array.filter的用法和ts中is的作用

interface A {a:number; } interface B {b:number; } type C A|B; const arr:C[{a:1},{b&#xff1a;2},{b:1}];当我要帅选出所有A类型数据时,用filter判断是否有a属性; const filterArrarr.filter(i> i.hasOwnProperty(a));此时你会发现 ts的推导类型并没有改变&#xff…

阿趣课堂丨一作解读,定量代谢组学临床应用

代谢组学是继基因组学和蛋白质组学之后新发展起来的一门学科&#xff0c;它通过对人体内小分子代谢物进行精准定性定量&#xff0c;分析代谢物与人体生理病理变化的关系&#xff0c;研究疾病发生发展、寻找疾病生物标记物、预测疾病预后等。 代谢组学在临床诊断上将有广阔的发…

Android系统的HAL层分析 (1)-- HAL的架构分析

目录 说明1. Android系统内为何会要有HAL &#xff1f;2. HAL_legacy和HAL对比3. HAL module的架构分析3.1 hw_module_t3.2 hw_module_methods_t3.3 hw_device_t 4. 分析hardware.c5. 分析HAL的加载过程6. 分析硬件访问服务7. 官方实例mokoid分析 说明 在Android系统中有一个很…

HDFS集群部署成功但网页无法打开如何解决(显示配置通过浏览器访问hdfs的端口)

在学习黑马2023大数据教程过程中&#xff0c;首先依照视频完成了如下配置&#xff1a;【必须】 【黑马2023大数据实战教程】大数据集群环境准备过程记录&#xff08;3台虚拟机&#xff09; 黑马2023大数据实战教程】VMWare虚拟机部署HDFS集群详细过程 最后node1的hadoop用户下…

共建智能汽车数据管理方案 | 4.15 IoTDB X EMQ 主题 Meetup 回顾

4 月 15 日&#xff0c;IoTDB X EMQ 智能汽车主题 Meetup 在上海成功举办。工业物联网时序数据库研发商天谋科技、物联网数据基础设施软件供应商 EMQ 的两位技术大牛&#xff0c;深度分享了企业如何应对智能汽车制造、智慧汽车联网平台普遍面临的海量车况、车产数据“采、存、取…

2023年Chat GPT 应用前景分析

从2022年12月初刚上线至今&#xff0c;不到半年时间ChatGPT月活就超过了1亿用户&#xff01;可谓火的一塌糊涂&#xff0c;比尔盖茨都称&#xff1a;ChatGPT的历史意义重大&#xff0c;不亚于PC或互联网诞生。以至于ChatGPT官网长期都处于满负荷运转的状态&#xff01; 由于Ch…

S7-200 SMART 和 S7-1200PLC进行PROFINET IO通信

从 S7-200 SMART V2.5 版本开始,S7-200 SMART 开始支持做 PROFINET IO 通信的智能设备。因此,两个 S7-200 SMART 之间可以进行 PROFINET IO 通信,一个CPU 作PROFINET IO 控制器,一个 CPU 作 PROFINET 通信的设备。组态的时候有两种方法,一种是通过硬件目录组态另外一种是通…

008+limou+CSS|CSS简介

1、CSS简介 &#xff08;1&#xff09;CSS是什么 指的是“Cascading Style Sheet&#xff08;层叠样式表&#xff09;”&#xff0c;是用来控制网页外观的一门技术 &#xff08;2&#xff09;CSS和CSS3 CSS历经CSS1.0、CSS2.0、CSS2.1、CSS3.0这几个版本&#xff0c;这几个…

java企业级信息系统开发学习笔记07 基于java配置方式使用Spring MVC

文章目录 一、学习目标二、基于java配置方式使用Spring MVC&#xff08;一&#xff09;创建Maven项目&#xff08;二&#xff09;添加相关依赖&#xff08;三&#xff09;创建日志属性文件&#xff08;四&#xff09;创建首页文件&#xff08;五&#xff09;创建Spring MVC配置…

学会Spring Batch 批处理框架,效率翻倍,上班轻松又快乐

一、SpringBatch 介绍 Spring Batch 是一个轻量级、全面的批处理框架&#xff0c;旨在支持开发对企业系统的日常操作至关重要的健壮的批处理应用程序。Spring Batch 建立在人们期望的 Spring Framework 特性&#xff08;生产力、基于 POJO 的开发方法和一般易用性&#xff09;…

uniapp-TypeError: Cannot convert undefined or null to object

问题详情 INFO Starting development server...ERROR TypeError: Cannot convert undefined or null to object TypeError: Cannot convert undefined or null to objectat Function.assign (<anonymous>)at uniapp-cli\node_modules\dcloudio\vue-cli-plugin-uni\lib…