一
概述
現在多核時代多線程開發越來越重要了
多線程相比於多進程有諸多優勢(當然也有諸多劣勢)
在早期C的庫中
有許多函數是線程不安全的
因為內部用到了靜態變量
比如
char *strtok(char *s
const char *delim)
該函數內部就有一個靜態指針
如果多個線程同時調用此函數時
可能就會出現奇怪的結果
當然也不是我們所想要的
現在LINUX對此函數功能有一個線程安全版本的接口
char *strtok_r(char *s
const char *delim
char **ptrptr)
這就避免了多個線程同時訪問的沖突問題
其實
如果保持 strtok()/
接口不變
同時還要保證線程安全
還有一個解決辦法
那就是采用線程局部變量
使用線程局部變量有兩種使用方式
一個稍微麻煩些
一個比較簡單
下面一一做個介紹(以LINUX為例)
二
線程局部變量的使用
比較麻煩些的使用方法用到的函數主要有三個
pthread_once(pthread_once_t*
void (*init_routine)(void))
pthread_key_create()/
pthread_setspecific()/
pthread_getspecific()/
其中 pthread_once 可以保證在整個進程空間init_routine函數僅被調用一次(它解決了多線程環境中使得互斥量和初始化代碼都僅被初始化一次的問題)
pthread_key_create 的參數之一指一個析構函數指針
當某個線程終止時該析構函數將被調用
並用對於一個進程內的給定鍵
該函數只能被調用一次
pthread_sespecific 和 pthread_getspecific 用來存放和獲取與一個鍵關聯的值
例子如下
pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;
static void destructor(void *ptr)
{
free(ptr)
}
void init_once(void)
{
pthread_key_create(&key
destructor)
}
static void *get_buf(void)
{
pthread_once(&once
init_once)
if ((ptr = pthread_getspecific(key)) == NULL) {
ptr = malloc(
)
pthread_setspecific(key
ptr)
}
return (ptr)
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf()
sprintf(ptr
hello world
)
printf(
》%s\n
ptr)
return (NULL)
}
void test(void)
{
int i
n =
;
pthread_t tids[
];
for (i =
; i < n; i++) {
pthread_create(&tids[i]
NULL
thread_fn
NULL)
}
for (i =
; i < n; i++) {
pthread_join(&tids[i]
NULL)
}
}
另外
還有一個更加簡單使用線程局部變量的方法
__thread 修飾符
(在WIN
平台下需要用
__declspec(thread) 修飾符
WIN
的東東總得要多寫幾筆
呵呵)
於是上述代碼可以修改如下
static void *get_buf(void)
{
static __thread void *ptr = malloc(
)
return (ptr)
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf()
sprintf(ptr
hello world
)
printf(
》%s\n
ptr)
return (NULL)
}
void test(void)
{
int i
n =
;
pthread_t tids[
];
for (i =
; i < n; i++) {
pthread_create(&tids[i]
NULL
thread_fn
NULL)
}
for (i =
; i < n; i++) {
pthread_join(&tids[i]
NULL)
}
}
看到沒有
這段代碼比前面一個簡單許多
但卻有一個問題
它存在內存洩露問題
因為當線程退出時各個線程分配的動態內存(ptr = malloc(
)) 並沒有被釋放
三
用ACL線程接口操作線程局部變量
為了解決上述問題
ACL庫中實現了線程局部變量的簡單釋放功能
acl_pthread_atexit_add(void *arg
void (*free_callback)(void*))
修改上述代碼如下
static void free_fn(void *ptr)
{
free(ptr)
}
static void *get_buf(void)
{
static __thread void *ptr = malloc(
)
acl_pthread_atexit_add(ptr
free_fn)
return (ptr)
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf()
sprintf(ptr
hello world
)
printf(
》%s\n
ptr)
return (NULL)
}
void test(void)
{
int i
n =
;
pthread_t tids[
];
for (i =
; i < n; i++) {
acl_pthread_create(&tids[i]
NULL
thread_fn
NULL)
}
for (i =
; i < n; i++) {
acl_pthread_join(&tids[i]
NULL)
}
}
ok
一切問題得到解決
細心的讀者會發現 pthread_create
pthread_join 前面都加了前綴
acl_
這是因為 ACL庫對線程庫進行了封裝
以適應不同平台下(UNIX
WIN
)下的使用
這個例子是跨平台的
WIN
下同樣可用
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27360.html