可重入性和线程安全性均与函数处理资源的方式有关。 但是,它们是不同的:

可重入函数既不会在连续调用中存储静态数据,也不会返回指向静态数据的指针。 对于这种类型的函数,调用方将提供函数所需的所有数据,如指向任何工作区的指针。 这意味着,函数的多个并发调用不会相互干扰。

注意:可重入函数不能调用非可重入函数。

线程安全函数使用锁 保护共享资源,以防止对其进行并发访问。 线程安全性只涉及函数实现方式,而不涉及其外部接口。 在 C 中,局部变量是在堆栈上动态分配的。 因此,任何不使用静态数据或其他共享资源的函数通常都是线程安全的。

在 ARM 库中,函数可能是线程安全的,如下所示:

某些函数从来都不是线程安全的,例如 setlocale()

某些函数在本质上就是线程安全的,例如 memcpy()

某些函数(例如 malloc())可通过实现 _mutex_* 函数变为线程安全的函数

其他函数仅在传递了适当参数时才是线程安全的,例如 tmpnam()。

如果应用程序以隐藏方式使用 ARM 库(如使用语言辅助函数),则可能会出现线程问题。

线程安全的函数

calloc(),
free(),
malloc(),
realloc() 
如果实现了 _mutex_* 函数,则堆函数是线程安全的。

在所有线程之间共享单个堆,并使用互斥量以避免进行并发访问时发生数据损坏。 每个堆实现都负责进行自己的锁定。 如果您提供了自己的分配器,它也必须进行自己的锁定。 这样,它便可进行精细锁定(如果需要),而不是简单地使用单个互斥量保护整个堆(粗放锁定)。
__alloca(),
__alloca_finish(),
__alloca_init(),
__alloca_initialize() 
如果实现了 _mutex_* 函数,则 alloca 函数是线程安全的。

每个线程的 alloca 状态包含在 __user_perthread_libspace 块中。 这意味着多个线程不会发生冲突。
请注意,alloca 函数也使用堆。 不过堆函数都是线程安全的。

abort(),
raise(),
signal(),
fenv.h 
ARM 信号处理函数和 FP 异常捕获是线程安全的。

信号处理程序和 FP 捕获设置是整个进程中的全局设置,并使用锁对其进行保护。 这样,即使多个线程同时调用 signal() 或 fenv.h 函数,也不会损坏数据。 但要注意,调用影响所有线程,而不是只影响调用线程。
clearerr(), fclose(),
feof(),ferror(), fflush(),
fgetc(),fgetpos(), fgets(),
fopen(),fputc(), fputs(),
fread(),freopen(), fseek(),
fsetpos(),ftell(), fwrite(),
getc(),getchar(), gets(),
perror(),putc(), putchar(),
puts(),rewind(), setbuf(),
setvbuf(),tmpfile(), tmpnam(),
ungetc() 
如果实现了 _mutex_* 函数,则 stdio 库是线程安全的。

每个单独的流都使用锁进行保护,因此,两个线程可以分别打开并使用其自己的 stdio 流,而不会相互干扰。

如果两个线程都要读取或写入相同的流,fgetc() 和 fputc() 级别的锁定可防止发生数据损坏,但是,每个线程的单独字符输出可能会交叉出现,因而容易造成混淆。
请注意,tmpnam() 也包含一个静态缓冲区,但仅在自变量为 NULL 时才使用它。 要确保 tmpnam() 使用是线程安全的,应提供您自己的缓冲区空间。

fprintf(), printf(),
vfprintf(), vprintf(), fscanf(),
scanf() 
使用这些函数时:

标准 C printf() 和 scanf() 函数使用 stdio,因而是线程安全的。

如果在多线程程序中调用标准 C printf(),其语言环境可能会发生变化。
clock() clock() 包含程序静态数据,此数据是在启动时一次性写入的,以后只能对其进行读取。 因此,clock() 是线程安全的,但前提是在初始化库时没有运行任何其他线程。
errno() 
errno 是线程安全的。

每个线程将其自己的 errno 存储在 __user_perthread_libspace 块中。 这意味着,每个线程可以单独调用 errno 设置函数,然后检查 errno,而不用担心受其他线程的影响。
atexit() 
atexit() 维护的退出函数列表是进程全局性的,并且使用锁对其进行保护。

在最坏的情况下,如果多个线程调用 atexit(),则不能保证调用退出函数的顺序。
abs(), acos(), asin(),atan(),
atan2(), atof(),atol(), atoi(),
bsearch(),ceil(), cos(),
cosh(),difftime(), div(),
exp(),fabs(), floor(),
fmod(),frexp(), labs(),
ldexp(),ldiv(), log(),
log10(),memchr(), memcmp(),
memcpy(),memmove(), memset(),
mktime(),modf(), pow(),
qsort(),sin(), sinh(),
sqrt(),strcat(), strchr(),
strcmp(),strcpy(), strcspn(),
strlcat(),strlcpy(), strlen(),
strncat(),strncmp(), strncpy(),
strpbrk(),strrchr(), strspn(),
strstr(),strxfrm(), tan(), tanh() 
这些函数在本质上就是线程安全的。
longjmp(), setjmp() 
虽然 setjmp() 和 longjmp() 在 __user_libspace 中保存数据,但它们均调用线程安全的 __alloca_* 函数。
remove(), rename(), time() 
这些函数使用中断,以便与 ARM 调试环境进行通信。 通常,必须为实际应用程序重新实现这些函数。
snprintf(), sprintf(),
vsnprintf(),vsprintf(), sscanf(),
isalnum(),isalpha(), iscntrl(),
isdigit(),isgraph(), islower(),
isprint(),ispunct(), isspace(),
isupper(),isxdigit(), tolower(),
toupper(),strcoll(), strtod(),
strtol(),strtoul(), strftime() 
使用这些函数时,这些基于字符串的函数将读取语言环境。 通常,它们是线程安全的。 但是,如果在会话中更改语言环境,则必须确保这些函数不受影响。

基于字符串的函数并不依赖于 stdio 库,例如,sprintf() 和 sscanf()。
stdin, stdout, stderr 这些函数是线程安全的。

FP 状态字

可以在多线程环境(甚至软件浮点)中安全地使用 FP 状态字。 其中,每个线程的状态字存储在其自己的 __user_perthread_libspace 块中。
Note

请注意,在硬件浮点中,FP 状态字存储在 VFP 寄存器中。 在这种情况下,线程切换机制必须为每个线程保留该寄存器的单独副本。
非线程安全的函数

setlocale() 
语言环境设置是所有线程的全局设置,并且未使用锁对其进行保护。 如果两个线程调用 setlocale(),则可能会发生数据损坏。 另外,很多其他函数读取当前语言环境设置,例如,strtod() 和 sprintf()。 因此,如果一个线程调用 setlocale(),另一个线程同时调用此函数,则可能会产生意外结果。

ARM 建议您选择所需的语言环境,然后调用一次 setlocale() 以对其进行初始化。 应在程序中创建任何其他线程之前执行此操作,以使任意数量的线程可以同时读取语言环境设置,而不会相互干扰。

请注意,localeconv() 不是线程安全的。 应改用指向用户提供的缓冲区的指针调用 ARM 函数 _get_lconv()。
asctime(), localtime(),
strtok() 
这些函数不是线程安全的。 每个函数都包含一个静态缓冲区,其他线程可能会在调用函数以及随后使用其返回值之间覆盖该缓冲区。

ARM 提供了可重入版本 _asctime_r()、_localtime_r() 和 _strtok_r()。 ARM 建议您改用这些函数以确保安全。
Note

这些可重入版本使用一些附加参数。_asctime_r() 使用的附加参数是指向输出字符串要写入的缓冲区的指针。_localtime_r() 使用的附加参数是指向结果要写入的 struct tm 的指针。_strtok_r() 使用的附加参数也是一个指针,指向的是指向下一个标记的 char 指针。
gamma()[1], lgamma() 这些扩展 mathlib 函数使用全局变量 _signgam,因此不是线程安全的。
mbrlen(), mbsrtowcs(),
mbrtowc(),wcrtomb(),
wcsrtombs() 
stdlib.h 中定义的 C89 多字节转换函数(如 mblen() 和 mbtowc())不是线程安全的,因为它们包含在所有线程之间共享而没有锁定的内部静态状态。

但是,wchar.h 中定义的扩展可重启版本(例如,mbrtowc() 和 wcrtomb())是线程安全的,但前提是您传入指向您自己的 mbstate_t 对象的指针。 如果要在处理多字节字符串时确保线程安全,这些函数只能使用非 NULL 的 mbstate_t * 参数。
exit() 
即使提供了基本 _sys_exit()(实际终止所有线程)的实现,也不要在多线程程序中调用 exit()。

在这种情况下,exit() 在调用 _sys_exit() 之前 先执行清除操作,因此会中断其他线程。
rand(), srand() 
这些函数保留全局性且不受保护的内部状态。 这意味着,rand() 调用从来都不是线程安全的。

ARM 建议您使用自己的锁定,以确保每次只有一个线程调用 rand(),例如,通过定义 $Sub$$rand()(如果要避免更改代码)。

或者,也可以执行以下操作之一:

提供您自己的随机数生成器,它可能具有多个独立实例

硬性规定只有一个线程需要生成随机数。

转载于:https://www.cnblogs.com/zhanglanyun/archive/2012/03/20/2407394.html

可重入性和线程安全性相关推荐

  1. 函数可重入性及编写规范

    一.可重入函数 1)什么是可重入性? 可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误.相反, 不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能 ...

  2. 函数可重入性(Reentrancy)概念详解

    1.什么是可重入性 重入一般可以理解为一个函数在同时多次调用,例如操作系统在进程调度过程中,或者单片机.处理器等的中断的时候会发生重入的现象. 可重入的函数必须满足以下三个条件: (1)可以在执行的过 ...

  3. 信号之函数的可重入性

    信号之函数的可重入性 在调用某个函数过程中出现中断信号,且改信号处理函数中再次调用该函数,访问全局.静态变量的函数是不可重入函数. 前后数据不一致,函数是不可重入的,特点:函数中使用全局变量或静态变量 ...

  4. 可重入函数 与线程安全的区别与联系

    线程安全:多个线程访问同一个区域的时候其最终结果是可预期的,并不会因为产生冲突或者异常中断再次恢复而使结果不可预期 1.重入:函数被不同的控制流程调用,有可能在第一次调用还没有返回的时候就再次进入该函 ...

  5. 什 么 是 可 重 入 性 , 为 什 么 说 Synchronized 是 可 重 入 锁 ?

    可 重 入 性 (1)可 重 入 性 是 锁 的 一 个 基 本 要 求 , 是 为 了 解 决 自 己 锁 死 自 己 的 情 况 .比 如 ,一 个 类 中 的 同 步 方 法 调 用 另 一 个 ...

  6. 可重入函数与线程安全的区别与联系

    本文主要介绍一下可重入函数与线程安全的区别与联系,在此之前我们先来了解一些基本概念:什么是线程全函数,什么是可重入函数? 线程安全函数 概念 线程安全的概念比较直观,一般来说,一个函数被称为线程安全的 ...

  7. 【Java 并发编程】多线程、线程同步、死锁、线程间通信(生产者消费者模型)、可重入锁、线程池

    并发编程(Concurrent Programming) 进程(Process).线程(Thread).线程的串行 多线程 多线程的原理 多线程的优缺点 Java并发编程 默认线程 开启新线程 `Ru ...

  8. Qt之可重入与线程安全

    本篇文章中,术语"可重入性"和"线程安全"被用来标记类与函数,以表明它们如何被应用在多线程应用程序中. 一个线程安全的函数可以同时被多个线程调用,甚至调用者会使 ...

  9. Java—重入锁的理解

    重入锁 (1)重进入: 1.定义:重进入是指任意线程在获取到锁之后,再次获取该锁而不会被该锁所阻塞.关联一个线程持有者+计数器,重入意味着锁操作的颗粒度为"线程". 2.需要解决两 ...

最新文章

  1. 巨石加密_点餐:如何吃一个可怕的巨石
  2. 嘉兴碧桂园云栖里土拍价格_嘉兴的碧桂园云栖里房子忍不住去看了房子看完我震惊了...
  3. 北京某打工子弟学校之一
  4. Android调试技巧之Eclipse行号和Logcat
  5. 【error】Invalid ADAPTORNAME specified. Type 'imaqhwinfo' for a list of available ADAPTORNAMEs.
  6. C#怎么用代码模拟手机去访问手机网站抓取数据
  7. .net解决Xss攻击
  8. linux-practice(23-24)
  9. c matlab 引擎调用,将C/C++回调传递给matlab引擎
  10. python 命名风格_python 代码风格------------PEP8规则
  11. 配置idea开发go编程语言并配置导入本地包
  12. 2021年安全员-B证新版试题及安全员-B证试题及解析
  13. 佐罗一键新机数据导出导入文件夹
  14. ps不更改原图比例,调整图片至任意尺寸
  15. TLF 使用详解!!
  16. 关于canvas生成图片的方法
  17. linux使用入门debian,Debian 7.7入门安装与配置
  18. 考研大作文模板与实战(图表为主+部分图画新题型)
  19. 华为鸿蒙如何添加桌面小组件,万能小组件怎么添加到桌面上
  20. 移动应用开发——“音乐”小程序项目

热门文章

  1. DBSCAN聚类算法初探(五)
  2. java为什么删除jpg删不掉_java-如何在不损失质量的情况下从图像(JPG)删除元数据?...
  3. canvas.drawBitmap()画出来的bitmap和原bitmap大小不同,有一部分缺失了
  4. ubuntu未发现wifi适配器_Windows 10 9月更新频翻车,1903版本网络适配器又出bug
  5. flutter 人脸检测_Flutter - 通过指纹和人脸识别认证
  6. PHP lareal_怎么样能在mysql里结合php的函数
  7. 由于找不到appvisvsubsystems32.dll_老实人就别找女朋友了 跟个老实人结婚有多累_新闻资讯...
  8. 加密Python脚本
  9. pytorch学习笔记(三十三):梯度下降和随机梯度下降
  10. 计算机导论知识点整理笔记(一.数据结构)