1. 建立線程api
int pthread_create (pthread_t* thread, pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);數組
thread:線程句柄,用於標示一個線程;安全
attr(線程屬性結構):函數
1.__detachstate(同步狀態):性能
PTHREAD_CREATE_JOINABLE:結合的狀態,在主線程退出以前,應該等待子線程完成;測試
PTHREAD_CREATE_DETACH:分離的狀態,和主線程分離,本身運行結束後並釋放資源;spa
2.__schedpolicy(調度策略):線程
SCHED_OTHER:正常、非實時,默認狀態unix
SCHED_RR:實時、輪轉法code
SCHED_FIFO:實時、先入先出
3.__schedparam:目前只有一個sched_priority(調度優先級)屬性
4.__inheritsched:
PTHREAD_EXPLICIT_SCHED:顯式指定調度策略和調度參數;
PTHREAD_INHERIT_SCHED:繼承調用線程的參數;
5.__scope:
PTHREAD_SCOPE_SYSTEM:與系統中全部線程一塊兒競爭CPU時間,默認值;
PTHREAD_SCOPE_PROCESS:與同進程中的線程競爭CPU;
start_routine:線程啓動函數;
arg: 線程啓動的時候,傳遞給啓動函數的參數,若是沒有就傳入0;
設置屬性的一些api:
pthread_attr_init(pthread_attr_t*)
pthread_attr_destroy(pthread_attr_t*)
pthread_attr_set-/get- 一系列函數
2. 線程的取消
一個線程能夠向另外一個線程發送取消請求,來終止另外一個線程的執行,不過另外一個線程能夠設置是否忽略取消請求,若是在不忽略的狀況下,根據線程的設置,多是執行到下一個取消點(Cancelation-point)才被終止執行,也多是當即終止執行。
線程的取消點:
pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函數以及read()、write()會引發系統阻塞的函數都是取消點。
對於沒有取消點的執行線程,咱們能夠在須要被取消執行的地方調用pthread_testcancel(),當一個線程接受到取消請求並無忽略的狀況下,一旦執行到pthread_testcancel()就會終止執行。
API:
int pthread_cancel(pthread_t); 向一個線程發送取消請求,成功返回0,不然返回非0,成功發送不表明已經完成取消;
int pthread_setcancelstate(int state, int *oldstate); 設置取消狀態,有PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE兩種,第一種是接受取消請求,第二種是忽略取消請求。oldstate若是不爲0,用於存入以前的取消狀態,以便未來能夠恢復。
int pthread_setcanceltype(int type, int *oldtype); 設置取消動做的時機,有PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS兩種,第一種是執行到下一個取消點終止,第二種是當即終止執行。oldtype若是不爲0,用於存入以前的值,以便未來能夠恢復。
注:在unix環境下測試,兩種狀況都是運行到下一個取消點或者有pthread_testcancel()的位置才終止,不清楚具體緣由。
3. 互斥鎖
因爲線程具備搶佔式和共享資源的特色,所以須要用一種互斥機制來保證線程之間的同步,最經常使用的就是互斥鎖。
建立互斥鎖:
靜態方式:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
動態方式:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); mutexattr缺省值爲0;
銷燬互斥鎖:
int pthread_mutex_destroy(pthread_mutex_t *mutex); 銷燬鎖佔用的資源,若是鎖定狀態則返回EBUSY;
鎖類型,在pthread_mutexattr_t(屬性)中指定:
PTHREAD_MUTEX_TIMED_NP:普通鎖(缺省值)。當一個線程加鎖之後,其他請求鎖的線程將造成一個等待隊列,並在解鎖後按優先級得到鎖;
PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖。容許同一個線程對同一個鎖成功得到屢次,並經過屢次unlock解鎖。若是是不一樣線程請求,則在加鎖線程解鎖時從新競爭;
PTHREAD_MUTEX_ERRORCHECK_NP:檢錯鎖。若是同一個線程請求同一個鎖,則返回EDEADLK,不然與PTHREAD_MUTEX_TIMED_NP類型動做相同。這樣就保證當不容許屢次加鎖時不會出現最簡單狀況下的死鎖;
PTHREAD_MUTEX_ADAPTIVE_NP:適應鎖。動做最簡單的鎖類型,僅等待解鎖後從新競爭;
鎖操做:
不管是什麼類型的鎖,都只能被一個線程獲取,不可能出現被兩個線程同時獲取到鎖的狀況。
普通鎖和適應鎖,解鎖者能夠是任何線程;
檢錯鎖則必須由加鎖者解鎖纔有效,不然返回EPERM;
嵌套鎖,文檔和實現要求必須由加鎖者解鎖;
int pthread_mutex_lock(pthread_mutex_t *mutex); 請求上鎖,若是沒有獲取到鎖,線程會被阻塞;
int pthread_mutex_unlock(pthread_mutex_t *mutex); 解鎖;
int pthread_mutex_trylock(pthread_mutex_t *mutex); 請求上鎖,若是成功得到一個鎖,返回0,不然返回非0,該函數當即返回,不會阻塞;
鎖機制並非線程安全的,若是在線程上鎖以後,解鎖以前,被取消,就可能出現死鎖的狀況。這種狀況須要使用線程退出回調函數來解鎖。
4. 條件變量
線程上鎖以後,應該儘量快的解鎖,好讓其餘線程能夠快速獲取鎖,以便提升程序性能。但不管如何,相關線程都會頻繁的循環檢測鎖狀態以便獲取鎖,例如可能在某種狀況下,某個線程會在大部分時間上鎖失敗,這樣不只消耗了cpu週期,並且也是無心義的檢測。所以,條件變量很好的改善了這種狀況。
條件變量是一種線程間的同步機制。主要利用了一個共享的條件變量來進行同步,一個線程等待條件變量進行掛起,而一個線程在條件成立的時候,喚醒阻塞的線程。爲了防止競爭和對資源的保護,條件變量老是和互斥鎖一塊兒使用。
建立條件變量:
靜態方式:pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
動態方式:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_destroy(pthread_cond_t *cond);
銷燬條件變量,若是條件變量在被等待,返回EBUSY;
使用條件變量:
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
首先對mutex解鎖(所以在調用pthread_cond_wait前,mutex必須處於鎖定狀態),而後等待cond的喚醒,此時該函數並不會當即返回而是被阻塞,直到cond被喚醒,而後對mutex上鎖,該函數返回。
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
和pthread_cond_wait相似,只是最有一個參數代表在規定時刻前條件沒有成立,返回ETIMEOUT,並結束等待。
int pthread_cond_signal(pthread_cond_t*);
喚醒隊列中第一個線程;
int pthread_cond_broadcast();
喚醒全部等待條件變量的線程;
因爲wait操做也是一個取消點,所以在wait操做結束以後,若是線程執行了取消操做,那麼mutex會被一直鎖定,形成死鎖。這情狀況須要使用退出回調函數來解鎖。
5. 終止線程
正常終止:函數調用return和執行pthread_exit(pthread_t),這種是代碼能夠控制的終止狀況;
異常終止:被其餘線程取消,或者線程執行發生異常;
異常終止可能會致使資源不能被正常釋放,其中包括鎖,若是鎖資源被解鎖前,線程就異常終止了,那麼其餘線程將永遠沒法再得到該鎖。
線程終止的清理:
使用pthread提供的清理函數(因爲清理函數是宏定義,必須在同一層做用域成對出現,才能編譯經過):
void pthread_cleanup_push(void (*routine) (void *), void *arg);
入棧一個清理函數和須要清理的對象;
void pthread_cleanup_pop(int execute);
在線程退出的時候,執行棧中的清理函數;
清理函數會在自動釋放資源,出如今函數對中的代碼段的任何終止動做,都將執行清理函數。
若是須要在線程終止時,自動釋放鎖資源,可使用:
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
......
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);
線程的終止:
void pthread_exit(void *retval);
線程終止本身的執行,並清理資源。
int pthread_join(pthread_t th, void **thread_return);
合併線程,若是是joinable的子線程,一般在主線程結束以前,必須調用該函數等待子線程的執行完成,然和合併到主線程。thread_return若是不爲0,保存線程的返回值。
int pthread_detach(pthread_t th);
分離線程,若是是detachable的子線程,它會在本身執行完成後,自動清理資源,主線程不須要等待它完成;
5. 線程私有數據(TSD)
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
該函數從TSD池中分配一項,將其值賦給key供之後訪問使用。若是destr_function不爲空,在線程退出(pthread_exit())時將以key所關聯的數據爲參數調用destr_function(),以釋放分配的緩衝區。
不論哪一個線程調用pthread_key_create(),所建立的key都是全部線程可訪問的,但各個線程可根據本身的須要往key中填入不一樣的值,這就至關於提供了一個同名而不一樣值的全局變量。
TSD池用一個結構數組表示:
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = { { 0, NULL } };
建立一個TSD就至關於將結構數組中的某一項設置爲"in_use",並將其索引返回給*key,而後設置destructor函數爲destr_function。
int pthread_key_delete(pthread_key_t key);
這個函數並不檢查當前是否有線程正使用該TSD,也不會調用清理函數(destr_function),而只是將TSD釋放以供下一次調用pthread_key_create()使用。在LinuxThreads中,它還會將與之相關的線程數據項設爲NULL(見"訪問")。
int pthread_setspecific(pthread_key_t key, const void *pointer);
void * pthread_getspecific(pthread_key_t key);
寫入(pthread_setspecific())時,將pointer的值(不是所指的內容)與key相關聯,而相應的讀出函數則將與key相關聯的數據讀出來。數據類型都設爲void *,所以能夠指向任何類型的數據。
實例:
// // main.c // threads // // Created by avl-showell on 16/7/13. // Copyright © 2016年 avl-showell. All rights reserved. // #include <stdio.h> #include <unistd.h> #include "pthread.h" pthread_t thread_1, thread_2, thread_3; pthread_cond_t cond; pthread_mutex_t mutex; pthread_key_t key; void* thread_1_func (void* arg) { static int i = 0; const char* data = "tread1"; pthread_setspecific(key, data); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0); pthread_cleanup_push(pthread_mutex_unlock, &mutex); while (1) { printf("thread 1 wait %s \n", pthread_getspecific(key)); //pthread_cond_wait(&cond, &mutex); int state = pthread_mutex_trylock(&mutex); if (state == 0) { printf("thread 1 start \n"); //sleep(1); pthread_testcancel(); i++; printf("thread 1 end %d \n", i); pthread_mutex_unlock(&mutex); } else { printf("thread 1 get lock failed... \n"); } } pthread_cleanup_pop(0); return 0; } void* thread_2_func (void* arg) { static int i = 0; const char* data = "tread2"; pthread_setspecific(key, data); while (1) { pthread_mutex_lock(&mutex); printf("thread 2 wait %s \n", pthread_getspecific(key)); pthread_cond_wait(&cond, &mutex); printf("thread 2 start \n"); //sleep(1); if (i == 0) { pthread_cancel(thread_1); } if (i == 8) { pthread_mutex_unlock(&mutex); pthread_exit(0); } i++; printf("thread 2 end %d \n", i); pthread_mutex_unlock(&mutex); } return 0; } void* thread_3_func (void* arg) { static int i = 1; while (1) { printf("thread 3 start \n"); sleep(1); pthread_cond_signal(&cond); if (i == 20) break; i++; printf("thread 3 end \n"); } return 0; } void destroy (void* arg) { printf("pthread destroy key \n"); } int main(int argc, const char * argv[]) { pthread_cond_init(&cond, 0); pthread_mutex_init(&mutex, 0); pthread_key_create(&key, destroy); if (pthread_create(&thread_1, 0, thread_1_func, 0)) { printf("thread 1 create failed... \n"); } if (pthread_create(&thread_2, 0, thread_2_func, 0)) { printf("thread 2 create failed... \n"); } if (pthread_create(&thread_3, 0, thread_3_func, 0)) { printf("thread 3 create failed... \n"); } //sleep(5); pthread_join(thread_1, 0); pthread_join(thread_2, 0); pthread_join(thread_3, 0); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); pthread_key_delete(key); printf("exit \n"); return 0; }