#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2)
對比兩個線程id是否相等
#include <pthread.h>
pthread_t pthread_self(void)
獲取自身的tid
3.創(chuàng)建線程
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg); 成功返回0,否則返回錯誤編號
當(dāng)函數(shù)成功返回的時候绳瘟,新創(chuàng)建的線程ID會被設(shè)置成tidp指向的內(nèi)存單元慨菱。
新創(chuàng)建的線程從start_rtn函數(shù)的地址開始運(yùn)行杖小,該函數(shù)只有一個無類型的指針參數(shù)arg列林。如果需要向start_rtn傳遞的參數(shù)有一個以上终吼,需要把這些參數(shù)放到一個結(jié)構(gòu)中贞盯,然后將結(jié)構(gòu)地址作為arg參數(shù)傳入
4.線程終止
1.線程正常返回音念,返回值是線程的退出碼
2.倍同一進(jìn)程中的其他線程取消
3.線程調(diào)用pthread_exit
#include <pthread.h>
void pthread_exit(void *rval_ptr)
int pthread_join(pthread_t thread, void **rval_ptr);
如果線程調(diào)用pthread_join 將會一直阻塞,直到指定的線程調(diào)用pthread_exit,或者從啟動歷程中返回或者被取消躏敢。
如果只是從它的啟動例程返回,rval_ptr就包含返回碼闷愤。如果線程被取消,rval_ptr指定的內(nèi)存單元會被設(shè)置成PTHREAD_CANCELED件余。如果不關(guān)心返回值讥脐,可以把rval_ptr設(shè)為NULL。
5.線程取消
#include <pthread.h>
int pthread_cancel(pthread_t tid);
//成功返回 0 否則返回錯誤編號
該函數(shù)并不等待線程終止啼器,只是提出請求
6.線程退出清理
#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);
觸發(fā)時機(jī):
當(dāng)調(diào)用pthread_exit時
響應(yīng)取消請求時
execute設(shè)置非0時
execute設(shè)置為0時旬渠,將刪除上一次 pthread_cleanup_push調(diào)用建立的程序
進(jìn)程函數(shù)與線程函數(shù)對比
7.線程同步
增量操作通常分三步
1.把內(nèi)存單元讀入寄存器
2.把寄存器中對變量做增量操作
3.把新的值寫回單元
8.互斥量(mutex)
本質(zhì)上是一把鎖,在訪問共享資源前對mutex進(jìn)行設(shè)置(加鎖)端壳,訪問完成后釋放(解鎖)互斥量告丢。
任何其他試圖再次對互斥量加鎖的都會被阻塞,直到當(dāng)前線程釋放互斥量损谦。如果釋放互斥量時有一個以上的線程阻塞岖免,那么所有該鎖上的阻塞線程都會變成可運(yùn)行狀態(tài)岳颇,第一個變?yōu)榭蛇\(yùn)行的線程就可以對互斥量加鎖。
如果允許其中的某一個線程在沒有加鎖的情況下訪問共享資源觅捆,那么互斥量就沒用了
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(phtread_mutex_t *mutex);
//成功返回0 否則返回錯誤編號
默認(rèn)初始化變量只需要把a(bǔ)ttr設(shè)置為NULL
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex)
//成功返回 0 否則返回錯誤編號
如果線程不希望被阻塞赦役,可以使用trylock嘗試對互斥量進(jìn)行加鎖,如果處于未鎖住狀態(tài)栅炒,就鎖住互斥量 掂摔,如果成功返回0,鎖住互斥量赢赊,否則失敗乙漓,返回EBUSY
#include <stdlib.h>
#include <pthread.h>
struct foo{
int f_count;
pthread_mutex_t f_lock;
int f_id;
};
struct foo*
fool_alloc(int id)
{
struct foo *fp;
if((fp = (struct foo*)malloc(sizeof(struct foo))) != NULL){
fp->f_count = 1;
fp->f_id = id;
if(pthread_mutex_init(&fp->f_lock,NULL) != 0){
free(fp);
return NULL;
}
}
return fp;
}
void
foo_hold(struct foo *fp)
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
}
void
foo_rele(struct foo *fp)
{
pthread_mutex_lock(&fp->f_lock);
if(--fp->f_count == 0){
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}
}
9.避免死鎖
如果線程試圖對同一個互斥量加鎖兩次,那么自身就會陷入死鎖狀態(tài)释移。
程序中使用一個以上的互斥量時叭披,如果允許一個線程一直占用第一個互斥量,并且試圖在鎖住第二個互斥量時處于阻塞狀態(tài)玩讳,擁有第二個互斥量的線程也在試圖鎖住第一個互斥量涩蜘,互相都不釋放鎖并且阻塞的情況。
假設(shè) 需要對互斥量AB同時加鎖熏纯。如果所有線程總是在互斥量B加鎖之前鎖住互斥量A同诫,那么使用這兩個互斥量就不會產(chǎn)生死鎖。
如果涉及太多的鎖和數(shù)據(jù)結(jié)構(gòu)樟澜,可以采用另外的方法误窖。在這種情況下,可以先釋放占有的鎖秩贰,然后過一段時間再試霹俺。這種情況可以使用pthread_mutex_trylock接口避免死鎖,如果已經(jīng)占用某些鎖 而且pthread_mutex_trylock接口返回成功毒费,那么就可以繼續(xù)丙唧。
#include <stdlib.h>
#include <pthread.h>
#define NHASH 29
#define HASH(id) (((unsigned long)id) % NHASH)
struct foo *fh[NHASH]; //pointer to array fh
pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
struct foo{
int f_count;
pthread_mutex_t f_lock;
int f_id;
struct foo * f_next; //pointer to next foo
/***more stuff.....***/
};
struct foo*
fool_alloc(int id)
{
struct foo *fp;
int idx;
if((fp = (struct foo*)malloc(sizeof(struct foo))) != NULL){ //allocate memory of object foo
fp->f_count = 1;
fp->f_id = id;
if(pthread_mutex_init(&fp->f_lock,NULL) != 0){
free(fp); //if mutex init error free memory of struct foo fp;
return NULL;
}
idx = HASH(id);
pthread_mutex_lock(&hashlock);
fp->f_next = fh[idx];
fh[idx] = fp;
pthread_mutex_lock(&fp->f_lock);
pthread_mutex_unlock(&hashlock);
pthread_mutex_unlock(&fp->f_lock);
}
return fp;
}
void
foo_hold(struct foo *fp)
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
}
struct foo*
foo_find(int id)
{
struct foo *fp;
pthread_mutex_lock(&hashlock);
for(fp = fh[HASH(id)]; fp != NULL; fp = fp->f_next){
if(fp->f_id == id) {
foo_hold(fp);
break;
}
}
pthread_mutex_unlock(&hashlock);
return fp;
}
void
foo_rele(struct foo *fp)
{
struct foo *tfp;
int idx;
pthread_mutex_lock(&fp->f_lock);
if(fp->f_count == 1){
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_lock(&hashlock);
pthread_mutex_lock(&fp->f_lock);
if(fp->f_count != 1){
fp->f_count--;
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_unlock(&hashlock);
return;
}
idx = HASH(fp->f_id);
tfp = fh[idx];
if(tfp == fp){
fh[idx] = fp->f_next;
}else
{
while(tfp->f_next != fp)
tfp = tfp->f_next;
tfp->f_next = fp->f_next;
}
pthread_mutex_unlock(&hashlock);
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}
else{
fp->f_count--;
pthread_mutex_destroy(&fp->f_lock);
}
}
上面的功能可以看出 鎖的顆粒度的大小嚴(yán)重影響多線程程序的復(fù)雜度 顆粒度越小 復(fù)雜度越大
10.pthread_mutex_timedlock
訪問已加鎖的互斥量的時候,允許綁定一個時間觅玻,如果在這個時間內(nèi)都沒有加上鎖想际,那么就會返回 ETIMEDOUT
11.讀寫鎖 (reader-writer lock)或者叫共享互斥鎖(shared-exclusive lock)
1.讀模式下加鎖 有多線程可以占有
2.寫模式下加鎖 一次只有一個線程可以占有
3.不加鎖
當(dāng)讀寫鎖 是處于寫加鎖狀態(tài)的時候,所有試圖訪問該鎖的線程都會阻塞串塑。
當(dāng)讀寫鎖 是處于讀加鎖狀態(tài)的時候沼琉,所有以讀模式對它進(jìn)行加鎖的線程都可以得到訪問權(quán)限,但是以寫模式對此進(jìn)行加鎖的線程都會阻塞桩匪,一直到所有線程都釋放他們的讀鎖位置 (當(dāng)以寫模式對此進(jìn)行加鎖到來的時候打瘪,后續(xù)進(jìn)行的讀模式進(jìn)行加鎖都會被阻塞,為了防止出現(xiàn)等待的寫模式鎖的請求一直得不到滿足的情況)
讀寫鎖非常適用于數(shù)據(jù)結(jié)構(gòu)的讀取遠(yuǎn)大于寫的情況
初始化:
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t * restrict rwlock,
const pthread_rwlockattr_t * restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
//成功返回0 否則返回錯誤編號
讀寫鎖操作:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
//成功返回0 否則返回錯誤編號
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
可以獲取鎖的時候返回0,否則返回EBUSY
代碼示例:
多個工作線程獲取單個主線程分配給他們的作業(yè)
#include <stdlib.h>
#include <pthread.h>
struct job {
struct job *j_next;
struct job *j_prev;
pthread_t j_id;
/** stuff here**/
};
struct queue{
struct job *q_head;
struct job *q_tail;
pthread_rwlock_t q_lock;
};
/*
*Initialize a queue
*/
int
queue_init(struct queue *qp)
{
int err;
qp->q_head = NULL;
qp->q_tail = NULL;
err = pthread_rwlock_init(&qp->q_lock,NULL);
if(err != 0)
{
return(err);
}
/**continue init data....**/
return 0;
};
/*
*Insert a job at the head of the queue.
*/
void
job_insert(struct queue *qp , struct job *jb)
{
pthread_rwlock_wrlock(&qp->q_lock); // lock with read mod
jb->j_next = qp->q_head;
jb->j_prev = NULL;
if(qp->q_head != NULL)
qp->q_head->j_prev = jb;
else
qp->q_tail = jb; /* list was empty*/
qp->q_head = jb;
pthread_rwlock_unlock(&qp->q_lock);
}
/*
* Append a job on the tail of the queue
*/
void
job_append(struct queue *qp , struct job *jb)
{
pthread_rwlock_wrlock(&qp->q_lock);
jb->j_next = NULL;
jb->j_prev = qp->q_tail;
if(qp->q_tail != NULL)
qp->q_tail->j_next = jb;
else
qp->q_head->j_next = jb;
qp->q_tail = jb;
pthread_rwlock_unlock(&qp->q_lock);
}
/*
*Remove the given job from a queue
*/
void
job_remove(struct queue *qp, struct job *jb)
{
pthread_rwlock_wrlock(&qp->q_lock);
if(jb == qp->q_head){ //if job at the head of a queue
qp->q_head = jb->j_next;
if(qp->q_tail == jb) // if only one job
qp->q_tail = NULL;
else
jb->j_next->j_prev = jb->j_prev;
}
else if (jb == qp->q_tail){
qp->q_tail = jb->j_prev;//if job at the head of a queue
jb->j_prev->j_next = jb->j_next;
} else {
jb->j_prev->j_next = jb->j_next;
jb->j_next->j_prev = jb->j_prev;
}
pthread_rwlock_unlock(&qp->q_lock);
}
/*
*Find a job by given thread ID
*/
struct job *
job_find(struct queue *qp, pthread_t id)
{
struct job *jp;
if(pthread_rwlock_rdlock(&qp->q_lock) != 0)
return NULL;
for(jp = qp->q_head ; jp != NULL ;jp = jp->j_next)
if(pthread_equal(jp->j_id,id))
break;
pthread_rwlock_unlock(&qp->q_lock);
return jp;
}
12.條件變量
條件變量本身是由互斥量保護(hù)的闺骚。所以改變條件狀態(tài)之前必須首先鎖住互斥量彩扔。
初始化:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
//成功返回0 否則返回錯誤編號
使用接口:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict tsptr);
//當(dāng)條件滿足時 返回0 否則 阻塞 如果錯誤返回錯誤編號
使用wait的時候 mutex必須是鎖住的,函數(shù)自動把調(diào)用線程放到等待條件的線程列表上僻爽,然后對互斥量進(jìn)行解鎖虫碉。
當(dāng)wait函數(shù)返回時,互斥量再次被鎖住胸梆。
通知條件已滿足
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond); //至少喚醒一個等待該條件線程
int pthread_cond_broadcast(pthread_cond_t *cond); //喚醒等待該條件的所有進(jìn)程
//成功返回0 錯誤返回錯誤編號
一定要在改變條件狀態(tài)后再給線程發(fā)信號
代碼示例
#include <pthread.h>
struct msg{
struct msg *m_next;
/*more stuff here*/
};
struct msg *workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
void
process_msg(void)
{
struct msg *mp;
for(;;){
pthread_mutex_lock(&qlock);
while(workq == NULL)
{
pthread_cond_wait(&qready, &qlock);
mp = workq;
workq = mp->m_next;
pthread_mutex_unlock(&qlock);
}
}
}
void
enqueue_msg(struct msg *mp)
{
pthread_mutex_lock(&qlock);
mp->m_next = workq;
workq = mp;
pthread_mutex_unlock(&qlock);
pthread_cond_signal(&qready);
}
process_msg中 先鎖住qlock互斥量敦捧,如果工作隊列wq為空,則需要等待qready條件碰镜,此時把qlock鎖釋放掉兢卵,線程阻塞,一直到有某個線程調(diào)用了 enqueue_msg 通知qready已經(jīng)ok绪颖,線程被喚醒 然后繼續(xù)執(zhí)行下面的工作處理邏輯秽荤。
13.自旋鎖
與互斥量相似,但是不是通過休眠使進(jìn)程阻塞柠横,而是在獲取鎖之前一直處于忙等(自旋)阻塞狀態(tài)窃款。
用于:鎖被持有的時間短。
#include <pthread.h>
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);
//成功返回0 否則返回錯誤編號
pshared參數(shù)表示進(jìn)程共享屬性牍氛,表面自旋鎖是如何獲取的晨继。
如果設(shè)置為:PTHREAD_PROCESS_SHARED 則該鎖可以被 可以訪問鎖底層內(nèi)存的線程 所獲取
否則設(shè)置為:PTHREAD_PROCESS_PRIVATE 只能被初始化該鎖的 進(jìn)程內(nèi)部的線程 所訪問
14.屏障
屏障允許每個線程等待,直到所有的合作線程都到達(dá)某個點(diǎn)糜俗,然后從該點(diǎn)繼續(xù)執(zhí)行踱稍。
#include <pthread.h>
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
const pthread_barrierattr_t *restrict attr,
unsigned int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);
int pthread_barrier_wait(pthread_barrier_t * barrier); //返回0 或者PTHREAD_BARRIER_SERIAL_THREAD 否則返回錯誤編號
count : 在允許所以線程繼續(xù)允許之前曲饱,必須到達(dá)屏障的線程數(shù)目悠抹。
調(diào)用wait函數(shù)的線程在屏障計數(shù),未滿足條件的時候扩淀,會進(jìn)入休眠狀態(tài)楔敌。如果這個線程是最后一個調(diào)用pthread_barrier_wait的線程,就滿足了屏障計數(shù)驻谆,所有的線程都被喚醒卵凑。
對于任一一個線程,如果wait函數(shù)返回了PTHREAD_BARRIER_SERIAL_THREAD胜臊。說明這個線程可以作為主線程勺卢,剩下的線程返回值只會為0。
一旦達(dá)到屏障計數(shù)值象对,并且線程處于非阻塞狀態(tài)黑忱,屏障就可以被復(fù)用。
但是在destroy之后,又調(diào)用init 時會對計數(shù)器進(jìn)行初始化甫煞。否則復(fù)用的時候計數(shù)器不會改變