首页 > 其他分享 >【嵌入式开发】线程安全与可重入的区别

【嵌入式开发】线程安全与可重入的区别

时间:2025-01-05 14:22:53浏览次数:1  
标签:重入 函数 int counter 嵌入式 线程 全局变量

一、线程安全

线程安全(Thread Safety)指的是在多线程环境中,程序的行为符合预期,不会出现数据不一致或状态不可预测的情况。反之,则是线程不安全。

线程安全问题主要来源于共享资源的并发访问,特别是当多个线程尝试修改同一资源时,如果没有适当的同步机制,就可能导致竞态条件(Race Condition)。

线程不安全的本质:模块内部有全局变量或静态变量。

举个例子:increment()函数就是线程不安全的,因为使用了全局变量。

#include <stdio.h>
#include <pthread.h>
 
#define NUM_THREADS 2
#define ITERATIONS 100000
 
// 全局变量
int counter = 0;
 
// 自增函数
void *increment(void *arg) {
    for (int i = 0; i < ITERATIONS; i++) {
        counter++;
    }
    pthread_exit(NULL);
}
 
int main() {
    // 定义线程数组
    pthread_t threads[NUM_THREADS];
 
    // 创建两个线程
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_create(&threads[i], NULL, increment, NULL);
    }
 
    // 等待线程执行完成
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }
 
    // 打印最终结果
    printf("Counter: %d\n", counter);
 
    return 0;
}

在这个例子中,有两个线程并发地对一个全局变量 counter 进行自增操作。由于多个线程同时访问和修改同一个全局变量,可能会导致竞争条件(Race Condition)和数据不一致的问题。

运行该程序可能会得到意料之外的结果,因为两个线程同时对 counter 进行自增操作,而没有进行同步控制,导致两个线程对 counter 的修改相互覆盖,最终导致 counter 的值不符合预期。

为了解决这个问题,可以使用互斥锁(Mutex)来保护临界区(Critical Section),确保在同一时间只有一个线程可以访问和修改共享资源。

二、可重入函数

可重入函数是一种在多线程或并发环境下能够安全地被多个线程同时调用的函数。它们的设计避免了对全局状态的依赖,确保在函数执行期间不会出现数据混乱或竞争条件。

可重入函数的分类

  1. 显式可重入函数:

    所有函数的参数都通过传值传递(没有使用指针)。

    所有数据引用都是本地的自动栈变量,即没有引用静态或全局变量。

    函数在任何调用情况下都可以被断言为可重入的。

  2. 隐式可重入函数:

    函数中某些参数通过引用传递(使用了指针)。

    当调用线程小心地传递指向线程私有数据的指针时,函数才可以被视为是可重入的。

可重入函数的特性:

可重入函数可以被一个或多个任务并发使用,而无需担心数据错误。

可重入函数可以在任何时刻被中断,稍后再继续运行,不会丢失数据。

可重入函数要么只使用本地变量,要么在使用全局变量时保护自己的数据。
  1. 不可重入函数

不可重入函数(Non-Reentrant Function)是指不能在多线程环境中安全调用的函数,因为它们可能会修改全局变量或静态变量,或者依赖于某些只初始化一次的资源。

不可重入函数的特点包括:

全局状态:使用全局变量或静态变量来存储状态。

唯一实例:依赖于程序中只存在一个实例的资源,如只初始化一次的文件句柄或通信端口。

顺序依赖:函数的执行依赖于特定的程序执行顺序。

当一个函数满足以下任意一个条件,则该函数是不可重入函数

函数内部使用了全局变量

函数内部使用了静态局部变量

函数返回值为全局变量或静态变量

函数内部使用了malloc或free函数

函数内部使用了标准 I/O 函数

函数内部调用了其它不可重入函数

原创 ZRQRS 知睿电子工程师

标签:重入,函数,int,counter,嵌入式,线程,全局变量
From: https://www.cnblogs.com/o-O-oO/p/18653326

相关文章

  • 2025年第16届蓝桥杯嵌入式竞赛学习笔记(二):点亮LED
    1.新建工程使用第一章配好的STM32CubeMX和Keil52.查看数据书册及图形化配置打开CT117E-M4产品手册查看LED灯的原理图LED的引脚为PC8-PC15,引脚为低电平时LED点亮U1为锁存器,锁存器的使能端PD2为高电平时,LED灯才会被点亮正确点灯步骤:①先PD2输出高电平②PC8-PC15输出低......
  • 嵌入式linux系统中CMake的基本用法
    第一:CMake的基本使用在上篇文章中,我们聊了聊Makefile。虽然它是C/C++项目编译的“老司机”,但写起来真的是让人头大。尤其是当项目文件一多,手写依赖就像在搬砖,费时又费力。那么问题来了,难道我们就没有更优雅的工具了吗?答案是:有!这时候,CMake就像一个专业的项目管家,它会帮......
  • 大白话拆解——多线程(五)(对小白很友好)
    前言:25年初,这个时候好多小伙伴都在备战期末我们新年第二天照样日更一篇,今天这篇一定会对小白非常有用的!!!因为我们会把案例到用代码实现的全过程思路呈现出来!!!我们一直都是以这样的形式,让新手小白轻松理解复杂晦涩的概念,把Java代码拆解的清清楚楚,每一步都知道他是怎么来的,为......
  • 进程线程和协程的区别?
    进程的特征独立性:进程是独立的执行单元,拥有自己的内存空间和系统资源并发性:多进程可以同时运行,彼此独立。动态性:进程是程序的一次执行过程,是动态产生和消亡的。资源拥有:进程拥有自己的资源,如内存、文件句柄等。进程的优缺点优点隔离性和稳定性:每个进程拥有独立的地址空间,......
  • 触想嵌入式工业一体机助力医疗行业打造国人智慧健康体系
    一、行业应用概述疼痛与发烧咳嗽一样,都是极常见的临床症状。受人口老龄化及现代生活压力加大影响,一些慢性痛症,如颈肩腰腿机能劳损导致的骨骼肌肉疼痛等,不仅在老龄群体中多发,更日趋年轻化。据统计,2020年我国慢性疼痛患者就已达3亿人之众,推动了康复科、疼痛科等特色科......
  • 复盘---我的第一次嵌入式软件工程师笔试_25/1/4
    复盘---我的第一次嵌入式软件工程师笔试_25/1/4总结​ 结果上来说,笔试的结果很差。自己三天准备的方向与实际笔试的题目偏差很大。通过网络上获取的笔试信息以及同学的经验,对比实际笔试还是有很大差异的。但根本原因还是自身的硬实力不够。有以下几个改善方向:通信协议方面,不能......
  • linux下进程或线程如何通信?
    管道fifo无名管道(内存文件):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程之间使用。进程的亲缘关系通常是指父子进程关系。有名管道(FIFO文件,借助文件系统):有名管道也是半双工的通信方式,但是允许在没有亲缘关系的进程之间使用,管道是先进先出的......
  • IO复用-代替多线程
    select//select(maxfd,rset,wset,eset,timeout);r读,w写,e错误,timeout多长时间轮询一次//有事件就返回//rset-->uLfds_bites[_FD_SIZE/(8*sizeof(long))]//#define_FD_SIZE1024默认值1024,内核定义fd_setrfds,rset;FD_ZERO(&rfds);FD_SET(sockfd,&rfds);intmaxf......
  • Python 中的多线程与多进程
    Python中的多线程与多进程引言在现代计算环境中,有效地利用计算机资源是提高应用程序性能和响应速度的关键。Python提供了两种主要的方式来进行并发编程:多线程(Multithreading)和多进程(Multiprocessing)。这两种方法都旨在通过并行执行任务来提升效率,但它们适用于不同的场景......
  • 25.Java JUC 引入(进程与线程、线程的状态、并发与并行、管程、用户线程与守护线程)
    一、JUC简介JUC是java.util.concurrent工具包的简称,这是一个处理线程的工具包,从JDK1.5开始出现二、进程与线程1、基本介绍(1)进程进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础在当代面向线程设......