一 概述
Pthread是一套通用的線程庫, 它廣泛的被各種Unix所支持, 是由POSIX提出的. 因此, 它具有很好的可移植性.
例1:
#include <</span>pthread.h>
void *pp(void *arg)
{
while (1) {
printf("%s/n", (char *)arg);
sleep(2);
}
return NULL;
}
main()
{
pthread_t pid;
pthread_create(&pid, NULL, pp, "hello world");
while (1) {
printf("I am main thread/n");
sleep(1);
}
}
執行:
gcc test.c -lpthread
./a.out
輸出:
I am main thread
hello world
I am main thread
hello world
............
二 返回值
也應該看到了, 每一個線程的返回值是void *.
有兩種方法返回:
1 return pointer;
2 pthread_exit(pointer);
這兩種方法是一樣的.
那麼, 其他的線程是如何得到這個返回值的呢?
用這個函數:
int pthread_join(pthread_t TH, void **thread_RETURN);
一個線程有兩種狀態, joinable 即系統保留線程的返回值, 直到有另外一個線程將它取走. detach系統不保留返回值.
下面的函數用於detach:
int pthread_detach (pthread_t TH);
pthread_t pthread_self(); 可以返回自己的id. 通常, 我們用下列的語句來detach自己:
pthread_detach(pthread_self());
三 Mutex
Mutex用於解決互斥問題. 一個Mutex是一個互斥裝置, 用於保護臨界區和共享內存. 它有兩種狀態locked, unlocked. 它不能同時被兩個線程所擁有.
下面的函數用於處理Mutex:
初始化一個Mutex
int pthread_mutex_init (pthread_mutex_t *MUTEX, const pthread_mutexattr_t *MUTEXATTR);
鎖定一個Mutex
int pthread_mutex_lock (pthread_mutex_t *mutex));
試圖鎖定一個Mutex
int pthread_mutex_trylock (pthread_mutex_t *MUTEX);
結鎖一個Mutex
int pthread_mutex_unlock (pthread_mutex_t *MUTEX);
銷毀一個Mutext
int pthread_mutex_destroy (pthread_mutex_t *MUTEX);
它的鎖一共有三種: "fast", "recursive", or "error checking"
進行lock操作時:
如處於unlock狀態, lock它, 即排斥占有。
在被其他線程lock的時候,
掛起當前線程, 直到被其他線程unlock
在已經被自己lock的時候,
"fast" 掛起當前線程.
"resursive" 成功並立刻返回當前被鎖定的次數
"error checking" 立刻返回EDEADLK
進行unlock操作時:
解鎖.
"fast" 喚醒第一個被鎖定的線程
"recursive" 減少lock數(這個數僅僅是被自己lock的, 不關其它線程的) 當lock數等於零的
時候, 才被unlock並喚醒第一個被鎖定的線程.
"error check" 會檢查是不是自己lock的, 如果不是返回EPERM. 如果是喚 醒第一個被鎖定的線程,
通常, 我們用一些靜態變量來初始化mutex.
"fast" `PTHREAD_MUTEX_INITIALIZER'
"recursive" `PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP'
"error check" `PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP'
注意: _NP 表示no portable不可移植
例如:
// "fast" type mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
... ...
pthread_mutext_lock(&mutex);
fwrite(buffer, 1, strlen(buffer), file);
pthread_mutex_unlock(&mutex);
... ...
四 Condition Variable (條件變量)
也是一種用於同步的device. 允許一個進程將自己掛起等待一個條件變量被改變狀態.
有下列幾個函數:
int pthread_cond_init (pthread_cond_t *COND,pthread_condattr_t *cond_ATTR);
int pthread_cond_signal (pthread_cond_t *COND);
int pthread_cond_broadcast (pthread_cond_t *COND);
int pthread_cond_wait (pthread_cond_t *COND, pthread_mutex_t *MUTEX);
int pthread_cond_timedwait (pthread_cond_t *COND, pthread_mutex_t *MUTEX, const struct timespec *ABSTIME);
int pthread_cond_destroy (pthread_cond_t *COND);
我想看看名字就可以知道它們的用途了. 通常我們也使用靜態變量來初始化一個條件變量.
Example:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_signal 用於喚醒一個被鎖定的線程.
pthread_cond_broadcast 用於喚醒所有被鎖定的線程.
pthread_cond_wait 用於等待.
為了解決競爭問題(即一個線程剛要去wait而另一個線程已經signal了), 它要與一個mutex連用.
看一看下面的例子:
int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//Waiting until X is greater than Y is performed as follows:
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
pthread_mutex_unlock(&mut);
pthread_cond_wait的執行過程如下:
1. 首先, 它unlock the mutex, then 掛起當前的線程.
2. 當被喚醒的時候, 它會lock the mutex.
這樣就保證了這是一個臨界區.
五 Thread-Specific Data (TSD)
說白了就是線程中使用的靜態變量. 大家可以很容易的理解為什麼使用靜態變量函數不是線程安全的(也就是它們一定要很小心的在線程中使用).
而使用靜態變量又是很方便的, 這就產生了 thread-specific data. 可以把它理解為一個指針數組, 但對於每個線程來說是唯一的.
Example:
int func()
{
char *p;
p = strdup(thread-specific-data[1]);
... ...
}
void *pthread-1(void *arg)
{
... ...
func()
... ...
}
void *pthread-2(void *arg)
{
... ...
func()
... ...
}
不同的線程調用func產生的結果是不同的. 這只是個例子.
int pthread_key_create(pthread_key_t *KEY, void (*destr_function) (void *));
int pthread_key_delete(pthread_key_t KEY);
int pthread_setspecific(pthread_key_t KEY, const void *POINTER);
void * pthread_getspecific(pthread_key_t KEY);
TSD可以看成是一個void *的數組.
注意: pthread_key_delete只是釋放key占用的空間, 你仍然需要釋放那個void *.
為了加深你的理解, 看一看下面的例子吧:
static pthread_key_t buffer_key;
static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;
void buffer_alloc(void)
{
pthread_once(&buffer_key_once, buffer_key_alloc);
pthread_setspecific(buffer_key, malloc(100));
}
char * get_buffer(void)
{
return (char *) pthread_getspecific(buffer_key);
}
static void buffer_key_alloc()
{
pthread_key_create(&buffer_key, buffer_destroy);
}
static void buffer_destroy(void * buf)
{
free(buf);
}
六. 信號處理
在線程中的信號處理是這個樣子, 所有的線程共享一組, 信號處理函數. 而每一個線程有自己的信號掩碼.
下面是用於處理線程信號的函數:
int pthread_sigmask (int HOW, const sigset_t *NEWMASK, sigset_t *OLDMASK);
int pthread_kill (pthread_t THREAD, int SIGNO);
int sigwait (const sigset_t *SET, int *SIG);
可以使用sigaction來安裝信號處理函數.
看一看下面的程序:
#include
#include
void *pp(void *)
{
printf("ha ha");
alarm(1);
}
void main_alarm(int i)
{
printf("Main got/n");
alarm(3);
}
main()
{
pthread_t pid;
struct sigaction aa;
sigset_t sigt;
sigfillset(&sigt);
aa.sa_handler = mainalarm;
aa.sa_mask = sigt;
aa.sa_flags = 0;
sigaction(SIGALRM, &aa, NULL);
pthread_create(&pid, NULL, pp, NULL);
while(1);
return 0;
}
七. 放棄 (Cancellation)
這是一種機制: 一個線程可以結束另一個線程. 精確的說, 一個線程可以向另一個線程發送 cancellation 請求. 另一個線程根據其設置, 可以忽略掉該請求, 也可以在到達一個cancellation點時, 來處理它。
當一個線程處理一個cancellaction請求時, pthread_exit 一個一個的調用 cleanup handlers. 所謂的一個cancellation點是在這些地方, 線程會處理cancellation請求. POSIX中的函數: pthread_join,pthread_cond_wait,pthread_cond_timewait,pthread_testcancel,sem_wait,sigwait 都是cancellation點. 下面的這些系統函數也是cancellation點:
accept open sendmsg
close pause sendto
connect read system
fcntl recv tcdrain
fsync recvfrom wait
lseek recvmsg waitpid
msync send write
nanosleep
其它的一些函數如果調用了上面的函數, 那麼, 它們也是cancellation點.
int pthread_setcancelstate (int STATE, int *OLDSTATE);
用於允許或禁止處理cancellation,
STATE可以是:PTHREAD_CANCEL_ENABLE PTHREAD_CANCEL_DISABLE
int pthread_setcanceltype (int TYPE, int *OLDTYPE);
設置如何處理cancellation, 異步的還是推遲的.
TYPE可以是:PTHREAD_CANCEL_ASYNCHRONOUS PTHREAD_CANCEL_DEFERRED
void pthread_testcancel (VOID);
八. 清理函數 (Cleanup Handlers)
這是一些函數, 它們會被pthread_exit按順序調用. 它們以棧風格被管理.
這種機制的目的是希望在退出前釋放掉一些占用的資源.
例如: 我們使用了一個MUTEX, 但希望在cancellation時能unlock它.
pthread_cleanup_push(pthread_mutex_unlock, (void *)&mut);
pthread_mutex_lock(&mut);
pthread_mutex_unlock(&mut);
pthread_cleanip_pop(0);
注意:
在異步處理過程中, 一個cancellation可以發生在pthread_cleaup_push 和pthread_mutex_lock之間. 這中情況是很糟糕的。所以,異步的cancellation 是很難用的。
void pthread_cleanup_push (void (*ROUTINE) (void *), void *ARG);
void pthread_cleanup_pop (int EXECUTE);
如果EXECUTE不等於0, 則在出棧後,會被執行一次。
九. 信號量 (Semaphores)
Semaphores是線程間共享的資源計數器。
基本的信號量操作為: 原子的增加信號量, 原子的減少信號量, 等待直到信號量的值為非零。
在POSIX中, 信號量有一個最大值, 宏SEM_VALUE_MAX定義了該值。在GNU的LIBC中, 該值等於INT_MAX (太大了)。
下面是相關的函數:
int sem_init (sem_t *SEM, int PSHARED, unsigned int VALUE);
初始化一個信號量, 其值為VALUE, PSHARED指明它是不是共享的.
0 表示local, 非0表示是全局的.
int sem_destroy (sem_t * SEM);
釋放掉相關的資源.
int sem_wait (sem_t * SEM);
等待直到SEM的值為非零.
int sem_trywait (sem_t * SEM);
int sem_post (sem_t * SEM);
將信號量加1.
int sem_getvalue (sem_t * SEM, int * SVAL);
取得信號量的值.
十 APIs
int
pthread_create(
pthread_t *tid , // 用於返回新創建線程的線程號.
const pthread_attr_t *attr ,
void*(*start_routine)(void*) , // start_routine 是線程函數指針,
// 線程從這個函數開始獨立地運行。
void *arg // arg 是傳遞給線程函數的參數。
);
//由於start_routine 是一個指向參數類型為void*,返回值為void*的指針,
//所以如果需要傳遞或返回多個參數時,可以使用強制類型轉化。
void
pthread_exit(
void* value_ptr
);
// 參數value_ptr 是一個指向返回狀態值的指針。
int
pthread_join(
pthread_t tid ,
void **status
);
// 參數tid 是希望等待的線程的線程號,status 是指向線程返回值的
//指針,線程的返回值就是pthread_exit 中的value_ptr 參數,或者是return
//語句中的返回值。該函數可用於線程間的同步。
int
pthread_mutex_init(
pthread_mutex_t *mutex,
const pthread_mutex_attr_t* attr
);
// 該函數初始化一個互斥體變量,如果參數attr 為NULL,則互斥
//體變量mutex 使用默認的屬性。
int
pthread_mutex_lock(
pthread_mutex_t *mutex
);
// 該函數用來鎖住互斥體變量。如果參數mutex 所指的互斥體已經
// 被鎖住了,那麼發出調用的線程將被阻塞直到其他線程對mutex 解鎖。
int
pthread_mutex_trylock(
pthread_t *mutex
);
// 該函數用來鎖住mutex 所指定的互斥體,但不阻塞。如果該互斥
//體已經被上鎖,該調用不會阻塞等待,而會返回一個錯誤代碼。
int
pthread_mutex_unlock(
pthread_mutex_t *mutex
);
// 該函數用來對一個互斥體解鎖。如果當前線程擁有參數mutex 所
// 指定的互斥體,該調用將該互斥體解鎖。
int
pthread_mutex_destroy (
pthread_mutex_t *mutex
);
// 該函數用來釋放分配給參數mutex 的資源。調用成功時返回值為
//0,否則返回一個非0 的錯誤代碼。
int
pthread_cond_init(
pthread_cond_t *cond,
const pthread_cond_attr_t*attr
);
// 該函數按參數attr指定的屬性創建一個條件變量。調用成功返回,
// 並將條件變量ID 賦值給參數cond,否則返回錯誤代碼。
int
pthread_cond_wait (
pthread_cond_t *cond ,
pthread_mutex_t*mutex
);
// 該函數調用為參數mutex 指定的互斥體解鎖,等待一個事件(由
// 參數cond 指定的條件變量)發生。調用該函數的線程被阻塞直到有其他
// 線程調用pthread_cond_signal 或pthread_cond_broadcast 函數置相應的條
// 件變量,而且獲得mutex 互斥體時才解除阻塞。
int
pthread_cond_timewait(
pthread_cond_t *cond ,
pthread_mutex_t*mutex ,
const struct timespec *abstime
);
// 該函數與pthread_cond_wait 不同的是當系統時間到達abstime 參
// 數指定的時間時,被阻塞線程也可以被喚起繼續執行。
int
pthread_cond_broadcast(
pthread_cond_t *cond
);
// 該函數用來對所有等待參數cond所指定的條件變量的線程解除阻
// 塞,調用成功返回0,否則返回錯誤代碼。
int
pthread_cond_signal(
pthread_cond_t *cond
);
// 該函數的作用是解除一個等待參數cond所指定的條件變量的線程
// 的阻塞狀態。當有多個線程掛起等待該條件變量,也只喚醒一個線程。
int
pthread_cond_destroy(
pthread_cond_t *cond
);
// 該函數的作用是釋放一個條件變量。釋放為條件變量cond 所分配的
// 資源。調用成功返回值為0,否則返回錯誤代碼。
int
pthread_key_create(
pthread_key_t key ,
void(*destructor(void*))
);
// 該函數創建一個鍵值,該鍵值映射到一個專有數據結構體上。如
//果第二個參數不是NULL,這個鍵值被刪除時將調用這個函數指針來釋放
//數據空間。
int
pthread_key_delete(
pthread_key_t *key
);
// 該函數用於刪除一個由pthread_key_create 函數調用創建的TSD
//鍵。調用成功返回值為0,否則返回錯誤代碼。
int
pthread_setspecific(
pthread_key_t key ,
const void(value)
);
// 該函數設置一個線程專有數據的值,賦給由pthread_key_create 創
// 建的TSD鍵,調用成功返回值為0,否則返回錯誤代碼。
void *
pthread_getspecific(
pthread_key_t *key
);
// 該函數獲得綁定到指定TSD 鍵上的值。調用成功,返回給定參數
//key 所對應的數據。如果沒有數據連接到該TSD 鍵,則返回NULL。
int
pthread_once(
pthread_once_t* once_control,
void(*init_routine)(void)
);
//該函數的作用是確保init_routine指向的函數,在調用pthread_once
//的線程中只被運行一次。once_control 指向一個靜態或全局的變量。