關於函數可重入與其線程安全之我討論(二)

可重入函數:       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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章