- 浏览: 141405 次
文章分类
最新评论
线程在遇到重入问题时与信号处理程序是类似的,在这两种情况下,多个控制线程在相同的时间有可能调用相同的函数。如果一个函数在相同的时间点可以被多个线程安全地调用,就称该函数是线程安全的。在 Single UNIX Specification 定义的函数中,下面这些是不能保证线程安全的(另外,ctermid 和 tmpnam 传入空指针参数时也不能保证线程安全)。
支持线程安全函数的操作系统会在<unistd.h>中定义符号_POSIX_THREAD_SAFE_FUNCTIONS。应用程序也可以在 sysconf 函数中传入 _SC_PTHREAD_SAFE_FUNCTIONS 参数在运行时检查是否支持线程安全函数。操作系统实现支持线程安全函数这个特性时,对 POSIX.1 中的一些非线程安全函数,它会提供可替代的线程安全版本。下表列出了这些函数的线程安全版本,它们在原来的名字后面加了“_r”,表明这些版本是可重入的(很多函数并不是线程安全的,因为它们返回的数据存放在静态的内存缓冲区中。通过修改接口,要求调用者自己提供缓冲区可以使函数变为线程安全)。
如果一个函数对多个线程来说是可重入的,则该函数是线程安全的,但这并不能说明对信号处理程序来说也是可重入的。如果函数对异步信号处理程序的重入是安全的,则可以说函数是异步信号安全的,在信号默认处理动作及可重入函数一节中提到的可重入函数就是异步信号安全的。
下面给出了一个 getenv 的可重入版本 getenv_r,它使用 pthread_once 函数来确保不管多少线程同时竞争调用 getenv_t,每个进程都只调用 thread_init 函数一次。
这里为了使该函数可重入而改变了其接口,调用者需要提供自己的缓冲区。而为了保证线程安全,使用了互斥量来保护环境在搜索请求的字符时不被修改。不仅如此,这里使用的还是可递归的互斥量,以保证它对信号处理程序也是可重入的。如果使用的是非递归的互斥量,则线程从信号处理程序中再用到该互斥量时就有可能出现死锁,因为在 getenv_r 中可能已经对其加锁了。
除了上表中的函数,POSIX.1 还提供了以线程安全的方式管理 FILE 对象的方法。可以使用 flockfile 和 ftrylockfile 获取给定 FILE 对象关联的锁,这个锁是递归的,这表示当占有这把锁的时候还可以再次获取该锁。
如果标准 I/O 例程都获取它们各自的锁,那么在做一次一个字符的 I/O 时就会出现严重的性能下降,因为这需要对每一个字符的读写操作进行获取锁和释放锁的动作。为避免这种开销,出现了如下不加锁版本的基于字符的标准 I/O 例程(不过除非被上面的 flockfile 类函数的调用包围,否则尽量不要使用,因为它们会导致不可预期的结果,比如由于多个控制线程非同步访问数据引起的种种问题)。
一旦对 FILE 对象进行加锁,就可以在释放锁前对这些函数进行多次调用,这样就可以在多次的数据读写上分摊总的加解锁的开销。
支持线程安全函数的操作系统会在<unistd.h>中定义符号_POSIX_THREAD_SAFE_FUNCTIONS。应用程序也可以在 sysconf 函数中传入 _SC_PTHREAD_SAFE_FUNCTIONS 参数在运行时检查是否支持线程安全函数。操作系统实现支持线程安全函数这个特性时,对 POSIX.1 中的一些非线程安全函数,它会提供可替代的线程安全版本。下表列出了这些函数的线程安全版本,它们在原来的名字后面加了“_r”,表明这些版本是可重入的(很多函数并不是线程安全的,因为它们返回的数据存放在静态的内存缓冲区中。通过修改接口,要求调用者自己提供缓冲区可以使函数变为线程安全)。
如果一个函数对多个线程来说是可重入的,则该函数是线程安全的,但这并不能说明对信号处理程序来说也是可重入的。如果函数对异步信号处理程序的重入是安全的,则可以说函数是异步信号安全的,在信号默认处理动作及可重入函数一节中提到的可重入函数就是异步信号安全的。
下面给出了一个 getenv 的可重入版本 getenv_r,它使用 pthread_once 函数来确保不管多少线程同时竞争调用 getenv_t,每个进程都只调用 thread_init 函数一次。
#include <stdlib.h> #include <string.h> #include <errno.h> #include <pthread.h> pthread_mutex_t env_mutex; static pthread_once_t initflag = PTHREAD_ONCE_INIT; static void thread_init(void){ pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&env_mutex, &attr); pthread_mutexattr_destroy(&attr); } extern char **environ; int getenv_r(const char *name, char buf[], int buflen){ // buf[0] = '\0'; int len = strlen(name); pthread_once(&initflag, thread_init); pthread_mutex_lock(&env_mutex); int i = 0; for(; environ[i] != NULL; i++){ if(strncmp(name, environ[i], len)==0 && environ[i][len]=='='){ if(strlen(&environ[i][len+1]) < buflen){ strcpy(buf, &environ[i][len+1]); pthread_mutex_unlock(&env_mutex); return 0; }else{ pthread_mutex_unlock(&env_mutex); return ENOSPC; } } } pthread_mutex_unlock(&env_mutex); return ENOENT; }
这里为了使该函数可重入而改变了其接口,调用者需要提供自己的缓冲区。而为了保证线程安全,使用了互斥量来保护环境在搜索请求的字符时不被修改。不仅如此,这里使用的还是可递归的互斥量,以保证它对信号处理程序也是可重入的。如果使用的是非递归的互斥量,则线程从信号处理程序中再用到该互斥量时就有可能出现死锁,因为在 getenv_r 中可能已经对其加锁了。
除了上表中的函数,POSIX.1 还提供了以线程安全的方式管理 FILE 对象的方法。可以使用 flockfile 和 ftrylockfile 获取给定 FILE 对象关联的锁,这个锁是递归的,这表示当占有这把锁的时候还可以再次获取该锁。
#include <stdio.h> int ftrylockfile(FILE *fp); /* 返回值:若成功,返回 0;若不能获取锁,返回非 0 数值 */ void flockfile(FILE *fp); void funlockfile(FILE *fp);
如果标准 I/O 例程都获取它们各自的锁,那么在做一次一个字符的 I/O 时就会出现严重的性能下降,因为这需要对每一个字符的读写操作进行获取锁和释放锁的动作。为避免这种开销,出现了如下不加锁版本的基于字符的标准 I/O 例程(不过除非被上面的 flockfile 类函数的调用包围,否则尽量不要使用,因为它们会导致不可预期的结果,比如由于多个控制线程非同步访问数据引起的种种问题)。
#include <stdio.h> int getchar_unlocked(void); int getc_unlocked(FILE *fp); /* 两个函数的返回值:若成功,返回下一个字符;若遇到文件尾或者出错,返回 EOF */ int putchar_unlocked(int c); int putc_unlocked(int c, FILE *fp); /* 两个函数的返回值:若成功,返回 c;否则,返回 EOF */
一旦对 FILE 对象进行加锁,就可以在释放锁前对这些函数进行多次调用,这样就可以在多次的数据读写上分摊总的加解锁的开销。
发表评论
-
打开伪终端设备
2018-07-09 20:50 1249在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1540伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 795多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 944终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 567尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 937虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 707tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 810终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 434终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 577在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 945在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 614在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 492消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 482有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 413FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 384管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 879readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 452POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 596记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 434UNIX 系统中,守护进程遵循下列通用惯例。 ...
相关推荐
### 可重入函数与线程安全函数 在多线程编程中,可重入函数与线程安全函数是两个非常重要的概念。了解这两个概念对于编写高质量、高性能的并发程序至关重要。 #### 可重入函数定义 可重入函数是指在多线程环境下...
在Linux多线程编程中,面临的一个主要挑战是线程重入问题,这主要源于早期UNIX系统设计时未充分考虑线程环境,许多库函数使用全局或静态数据,导致线程安全问题。以下是对这个问题的详细阐述: 1. **线程重入与`...
线程安全和可重入性是多线程编程中的核心概念,它们对于保证并发环境下的程序正确性和性能至关重要。线程安全是指一个函数或方法在多线程环境下被调用时,能够正确处理共享资源,不会因为线程间的交互而导致错误的...
在LabVIEW(Laboratory Virtual Instrument Engineering Workbench)中,可重入VI(Reentrant VI)是一种特殊类型的虚拟仪器,设计用于解决多线程环境下的并发访问问题,确保数据的完整性和程序的正确运行。...
在Java多线程高并发编程中,重入锁(ReentrantLock)是一个至关重要的概念,它提供了比Java内置锁(synchronized)更细粒度的控制,并且具有更高的可读性和可扩展性。本篇文章将深入探讨重入锁的相关知识点。 首先...
函数的可重入性关系到程序能否在多线程或者中断服务中安全地执行,以及能否在多任务环境下被多个任务安全地调用。下面将详细介绍可重入函数与不可重入函数的概念、特性以及如何编写和转换这两种类型的函数。 可重入...
可重入锁的主要特性是它允许一个已经持有锁的线程再次请求该锁,而不被阻塞。这种设计是为了避免死锁情况的发生。当一个线程在执行过程中需要进入一个已被自己持有的锁保护的代码块时,如果锁是可重入的,那么它就...
测试应覆盖各种边界情况,例如单线程、多线程竞争、重入、锁升级等,以充分验证我们的实现。 总之,可重入锁是Java并发编程中不可或缺的一部分,理解和实现它有助于深入掌握线程安全的概念和机制。通过动手实践,...
1. **可重入性**:Lock支持线程重入,意味着一个已获得锁的线程可以再次请求同一把锁。这与`synchronized`相同。 2. **非块结构**:使用Lock时,我们不需要像`synchronized`那样包围整个代码块,而是需要在适当的...
可重入性是指一个函数可以被多个任务或线程同时调用而不会出现数据错误的情况。这种特性对于多线程编程尤为重要,因为它能够保证在并发环境下程序的正确性和稳定性。 **1.2 可重入函数的特点** - **不持有静态数据...
可重入函数的优点是可以提高程序的执行效率和可靠性,但需要遵循一定的编写规范和规则,以确保函数的可重入性和线程安全性。 了解可重入函数的概念和编写规范,对于提高程序的执行效率和可靠性具有重要意义。
在计算机科学领域中,“可重入”(reentrant 或 re-entrant)是一个重要的概念,尤其在多线程编程和并发环境中尤为关键。如果一个程序或者子程序能够被安全地并行执行,那么我们称这个程序或子程序为“可重入”的。...
如果线程在持有锁的过程中再次请求锁,`ReentrantLock`允许线程重入,这就是“可重入”的含义。释放锁时,`state`会递减,直到`state`变为0,表示锁已经被完全释放。 在等待队列中,线程会进入阻塞状态,等待被唤醒...
在读写操作中,通常会用到`ReentrantLock`(可重入锁)配合`Condition`来实现对共享资源的精确控制,确保在特定时刻只有一个线程进行读或写操作,从而避免数据不一致。 线程控制的实践不仅关乎性能,还直接影响程序...
在不支持重入的情况下,这意味着一旦线程获得了读锁或写锁,它无法再次获取同一类型的锁,直到释放当前持有的锁。这种设计有助于防止死锁,并在某些情况下提高并发性能。 读写锁的核心概念包括读锁和写锁。读锁用于...
线程安全 VS 可重入什么是线程安全和可重入常见的线程不安全的情况(重点)常见的线程安全的情况(重点)常见的可重入情况常见不可重入的情况可重入与线程安全联系可重入与线程安全区别(重点) 什么是线程安全和可重入 ...
- **线程重入和openmp标准**: 线程重入函数可以在多线程环境中安全调用,OpenMP 是一个多线程编程的标准。 #### 五、进程间通信 - **管道**: 管道是一种进程间通信方式,允许一个进程的输出作为另一个进程的输入...
`ReentrantLock`可重入锁提供了更灵活的控制,支持公平锁和非公平锁策略。 线程优先级是调度的重要依据,Java线程有三个基本优先级:`Thread.MIN_PRIORITY`(最低优先级)、`Thread.NORM_PRIORITY`(正常优先级)和`...
Java提供了多种同步机制,如synchronized关键字、wait()和notify()方法、ReentrantLock重入锁、Semaphore信号量等。synchronized用于保证同一时刻只有一个线程访问共享资源,而wait()和notify()用于线程间的通信,...