
可重入性和线程安全性都与该函数如何处理资源有关. 但是,它们是不同的:
重入函数既不会在连续调用中存储静态数据,也不会返回指向静态数据的指针. 对于此类功能,调用者将提供该功能所需的所有数据,例如指向任何工作区的指针. 这意味着对一个函数的多个并发调用不会互相干扰.
注意: 可重入函数不能调用非可重入函数.
线程安全功能使用锁来保护共享资源,以防止并发访问它们. 线程安全仅涉及功能的实现方式,而不涉及其外部接口. 在C语言中,局部变量在堆栈上动态分配. 因此,任何不使用静态数据或其他共享资源的函数通常都是线程安全的.
在ARM库中,函数可能是线程安全的,如下所示:
某些函数永远都不是线程安全的,例如setlocale()
某些函数本质上是线程安全的,例如memcpy()
通过实现_mutex_ *函数,可以使某些函数(例如malloc())成为线程安全的
其他函数仅在传递适当的参数(例如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是线程安全的.
每个线程在__user_perthread_libspace块中存储自己的errno. 这意味着每个线程可以单独调用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块中.
注意
请注意,在硬件浮点中,FP状态字存储在VFP寄存器中. 在这种情况下,线程切换机制必须为每个线程保留该寄存器的单独副本.
非线程安全功能

setlocale()
语言环境设置是所有线程的全局设置,并且不受锁保护. 如果两个线程调用setlocale(),则可能会发生数据损坏. 另外,许多其他函数读取当前的语言环境设置,例如strtod()和sprintf(). 因此,如果一个线程同时调用setlocale(),而另一个线程同时调用此函数,则可能会产生意外的结果.
ARM建议您选择所需的语言环境,然后调用一次setlocale()对其进行初始化. 应该在程序中创建任何其他线程之前完成此操作,以便任意数量的线程可以同时读取语言环境设置而不会互相干扰.
请注意,localeconv()并非线程安全的. 而是使用指向用户提供的缓冲区的指针来调用ARM函数_get_lconv().
asctime(),localtime(),
strtok()
这些函数不是线程安全的. 每个函数都包含一个静态缓冲区,在调用该函数与随后使用其返回值之间,其他线程可能会覆盖该缓冲区.
ARM提供可重入版本_asctime_r(),_ localtime_r()和_strtok_r(). ARM建议您改用这些功能以确保安全.
注意
这些可重入版本使用一些附加参数. _asctime_r()使用的附加参数是指向要在其中写入输出字符串的缓冲区的指针. _localtime_r()使用的附加参数是指向要写入结果的结构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()(如果要避免代码更改).
或者,您也可以执行以下操作之一:
提供您自己的随机数,该可能具有多个独立的实例
强制性要求只有一个线程需要生成一个随机数.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/tongxinshuyu/article-246734-1.html
真是弱爆