关于函数可重入与其线程安全之我讨论(二)

本文深入探讨了可重入函数与线程安全函数的区别与联系,通过实例展示了如何将非线程安全函数改造为线程安全函数,并介绍了可重入化的实现方式,包括使用递归锁和键绑定等技术手段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可重入函数:       Reentrant Function
线程安全函数:   Thread-Safe Function


可重入和线程安全不是一个概念。
可重入 => 线程安全
可重入函数要解决的问题是,不在函数内部使用静态或全局数据,不返回静态或全局数据,也不调用不可重入函数。 
线程安全函数要解决的问题是,多个线程调用函数时访问资源冲突。
函数如果使用静态变量,通过加锁后可以转成线程安全函数,但仍然有可能不是可重入的,比如strtok。 
strtok是既不可重入的,也不是线程安全的。 

加锁的strtok不是可重入的,但线程安全。 

而strtok_r既是可重入的,也是线程安全的。 



为了更加深刻的理解,我们看unix高级编程上的一个例子:

#include <limits.h>
#include <string.h>
static char envbuf[ARG_MAX];
extern char **environ;

char *
getenv(const char *name)
{
int i, len;
len = strlen(name);
for (i = 0; environ[i] != NULL; i++) {
if ((strncmp(name, environ[i], len) == 0) &&
 (environ[i][len] == '=')) {
strcpy(envbuf, &environ[i][len+1]);
return(envbuf);
}
}
return(NULL);
}

这个函数是获取进程的环境变量,但是这个实现用到了全局变量,既不是线程安全的,当然也是不可重入的,如果有两个线程同时调用这个函数,

就可能有数据冲突,可以对这个函数加以改进用互斥锁使其变成线程安全的,如下:

#include <limits.h>
#include <string.h>
static char envbuf[ARG_MAX];
pthread_mutex_t env_mutex;
extern char **environ;

char *
getenv(const char *name)
{
int i, len;
pthread_mutex_init(&env_mutex, NULL);
len = strlen(name);

pthread_mutex_lock(&env_mutex);
for (i = 0; environ[i] != NULL; i++) {
if ((strncmp(name, environ[i], len) == 0) &&
  (environ[i][len] == '=')) {
strcpy(envbuf, &environ[i][len+1]);

pthread_mutex_unlock(&env_mutex);
return(envbuf);
}
}

pthread_mutex_unlock(&env_mutex);
return(NULL);
}

但是这并不能保证这是课重入的,考虑到奥这个函数的特殊性,只有读取,没有写入得特点,我们可以对其可重入化,为了达到我们期望的结果,要用到递归锁。

如下:

#include <string.h>

#include <errno.h>
#include <pthread.h>
#include <stdlib.h>

extern char **environ;

pthread_mutex_t env_mutex;
static pthread_once_t init_done = 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);
}

int
getenv_r(const char *name, char *buf, int buflen)
{
int i, len, olen;
pthread_once(&init_done, thread_init);
len = strlen(name);
pthread_mutex_lock(&env_mutex);
for (i = 0; environ[i] != NULL; i++) {
if ((strncmp(name, environ[i], len) == 0) &&
 (environ[i][len] == '=')) {
olen = strlen(&environ[i][len+1]);
if (olen >= buflen) {
pthread_mutex_unlock(&env_mutex);
return(ENOSPC);
}
strcpy(buf, &environ[i][len+1]);
pthread_mutex_unlock(&env_mutex);
return(0);
}
}
pthread_mutex_unlock(&env_mutex);
return(ENOENT);
}

这个实现为了使得函数为可重入,我们改变了接口,调用者必须自己提供缓冲区,这样每个线程使用各自的缓冲区,

避免其他线程的干扰;而且我们使用了递归锁,如果不使用递归锁,则会导致死锁,因为如果信号处理程序在线程执行gentenv_r

时中断了该线程,由于这是已经占有锁的env_mutex,这样其他线程试图对这个互斥量进行加锁就会阻塞,最终进入死锁状态。


再则,如果应用函数不允许修改函数接口,还可以创建key,对线程私有数据进行绑定来使函数线程安全化,或线程可重入化。










文章出处:飞诺网(www.diybl.com):http://www.diybl.com/course/3_program/c++/cppjs/20071222/92989.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值