歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

線程同步(6):linux 多線程pthread中的各種函數

一 概述

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 指向一個靜態或全局的變量。

Copyright © Linux教程網 All Rights Reserved