1. void 和void* 的使用數據結構
void 意思是無類型,void* 意思是無類型指針,能夠指向全部數據類型。函數
(1) void 主要用於對函數參數和返回值的限定,加上void 限定則代表該函數不接受參數或者無返回值(由於在C語言中,即便函數聲明時無參數,在函數調用時傳進參數是不會報錯的,即便函數沒有指定返回類型,函數會默認返回int類型,因此加上void 限定是十分有必要的)ui
(2) void* 則多用於函數參數(或返回值)能夠是任意類型的狀況下,由於void* 能夠接受任意類型而無需強制裝換,但將其轉換爲具體類型則須要強制裝換,這有點相似於C++中的繼承關係。spa
2. 線程建立與線程標識操作系統
線程ID 用pthread_t 數據結構來表示,在不一樣的操做系統中表示方法不一樣,因此可移植的操做系統實現不能把它做爲整數處理,所以必須使用 pthread_equal() (函數原型:int pthread_equal(pthread_t tid1, pthread_t tid2);)函數來對像個線程ID 進行比較。調用 pthread_self 函數能夠獲取自身線程ID。線程
在傳統Unix進程模型中,每一個進程只有一個線程。在POSIX線程的狀況下, 當程序開始運行時,也是以單進程中的單個控制線程啓動的,並能夠經過pthread_create函數建立新線程。(函數原型: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rth)(void*), void *restrict arg);) 其中tidp爲 ID,attr爲訂製線程屬性,start_rth 是線程開始運行的函數地址,arg是該函數的參數(若由多個參數,則使用結構體)指針
《APUE》 程序清單 11-1 打印線程ID:rest
1 #include "apue.h" 2 #include <pthread.h> 3 4 pthread_t ntid; 5 6 void printids(const char* s) { 7 pid_t pid; 8 pthread_t tid; 9 10 pid = getpid(); 11 tid = pthread_self(); 12 printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid, 13 (unsigned int)tid, (unsigned int)tid); 14 } 15 16 void* thr_fn(void* arg) { 17 printids("new thread: "); 18 return ((void*)0); 19 } 20 21 int main() 22 { 23 int err; 24 err = pthread_create(&ntid, NULL, thr_fn, NULL); 25 if (err != 0) 26 err_quit("can't create thread: %s\n", strerror(err)); 27 printids("main thread: "); 28 29 sleep(1); 30 exit(0); 31 }
程序運行結果:code
main thread: pid 20961 tid 4253615936 (0xfd890740)
new thread: pid 20961 tid 4245337856 (0xfd0ab700)
進程ID 相同, 線程ID 不一樣blog
3. 線程終止
終止線程有三種方式,在不終止整個進程的狀況下終止它的控制流(注意進程中任一線程調用了exit, _Exit 或者_exit ,那麼整個進程就會終止)
有時程序要了保證在主進程結束前線程運行結束,會在主進程中調用sleep。也可使用pthread_join (函數原型: int pthread_join(pthread_t thread, void** rval_ptr); )
函數,調用該函數時,調用線程會一直阻塞,直到指定的線程調用pthread_exit,從啓動例程中返回或者別取消。另外經過pthread_join函數還能夠訪問到 pthread_exit()
參數中的rval_ptr 指針。
4. 線程清理處理程序(thread cleanup handler)
線程能夠安排它退出時須要調用的函數,這些處理程序記錄在棧中,可使用pthread_cleanup_push() 函數註冊多個清理處理程序。
在下面三種狀況下,已經註冊的清理處理程序將會被調用
5. 線程同步
在變量修改時間多於一個存儲器(非原子性)訪問週期的處理器結構中,且當存儲器讀與存儲器寫這兩個週期交叉時,潛在的不一致性就會出現。若是修改操做是原子操做,那麼就不存在競爭。
*互斥量:能夠經過pthread的虎吃接口保護數據,確保同一時間只有一個線程訪問數據。
pthread_mutex_t 互斥變量數據類型
pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t * restrict attr) 對互斥量進行初始化,attr = NULL 則使用默認屬性;也可以使用常量
PTHREAD_MUTEX_INITIALIZER 進行初始化
pthread_mutex_destroy(pthread_mutex_t* mutex); 動態分配的互斥量(如malloc),釋放內存前要調用該函數
pthread_mutex_lock(phtread_mutex_t* mutex); 對互斥量進行加鎖
pthread_mutex_unlock(phtread_mutex_t* mutex); 對互斥量解鎖
pthread_mutex_trylock(pthread_mutex_t* mutex) 嘗試加鎖,未阻塞返回0, 不然失敗返回EBUSY
* 避免死鎖
死鎖能夠經過當心地控制互斥量加鎖的順序來避免。
如下代碼中,每一個foo 結構都由 一個f_lock 互斥量來保護該數據結構的引用次數,另外還有一個hashlock 來維護一個用於跟蹤foo數據結構的散列列表,這裏爲了不死鎖須要當心地維護f_lock 和hashlock 的加鎖順序。
《APUE》程序清單 11-6 使用兩個互斥量
#include <stdlib.h> #include <pthread.h> #define NHASH 29 #define HASH(fp) (((unsigned long)fp)%NHASH) struct foo* fh[NHASH]; pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER; struct foo { int f_count; pthread_mutex_t f_lock; struct foo* f_next; int f_id; }; struct foo* foo_alloc(void) { struct foo* fp; int idx; if ((fp = malloc(sizeof(struct foo))) != NULL) { fp->f_count = 1; if (pthread_mutex_init(&fp->f_lock, NULL) != NULL) { free(fp); return (NULL); } idx = HASH(fp); 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; int idx; idx = HASH(fp); pthread_mutex_lock(&hashlock); for (fp = fh[idx]; 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); /* need to recheck the condition */ if (fp->f_count != 1) { fp->f_count--; pthread_mutex_unlock(&fp->f_lock); pthread_mutex_unlock(&hashlock); return; } /* remove from list */ idx = HASH(fp); tfp = fh[idx]; if (tfp == fp) { fh[idx] = fp->f_next; } else { while (tfp != fp) tfp = tfp->f_next; tfp->f_next = fp->f_next; } pthread_mutex_unlock(&hashlock); pthread_mutex_unlock(&fp->f_lock); pthread_mutex_destory(&fp->f_lock); } else { fp->f_count--; pthread_mutex_unlock(&fp->f_lock); } }
程序11-6 能夠只經過hashlock 來保護引用計數來使事情大大簡化,而結構互斥量f_lock 則可用來保護foo結構中的其餘任何東西。