線程
一、概念:
線程就是程序的執行路線,即進程內部的控制序列,或者說是進程的子任務。
線程,輕量級,不擁有自己獨立的內存資源,共享進程的代碼區、數據區、堆區(注意沒有棧區)、環境變量和命令行參數、文件描述符、信號處理函數、當前目錄、用戶ID和組ID等資源。
線程擁有自己獨立的棧,因此也有自己獨立的局部變量。
一個進程可以同時擁有多個線程,即同時被系統調度的多條執行路線,但至少要有一個主線程。
二、線程函數
1.創建線程
int pthread_create (pthread_t* restrict thread,
const pthread_attr_t* restrict attr,
void* (*start_routine) (void*),
void* restrict arg);
thread- 線程ID,輸出參數。
pthread_t即unsigned long int。
attr - 線程屬性,NULL表示缺省屬性。
pthread_attr_t可能是整型也可能是結構,
因實現而異。
start_routine- 線程過程函數指針,
參數和返回值的類型都是void*。
啟動線程本質上就是調用一個函數,
只不過是在一個獨立的線程中調用的,
函數返回即線程結束。
arg - 傳遞給線程過程函數的參數。
線程過程函數的調用者是系統內核,
而非用戶代碼,
因此需要在創建線程時指定參數。
2.等待線程
int pthread_join (pthread_t thread, void** retval);
等待thread參數所標識的線程結束,成功返回0,失敗返回錯誤碼。
3.獲取線程自身的ID
pthread_t pthread_self (void);
成功返回調用線程的ID,不會失敗。
4.比較兩個線程的ID
int pthread_equal (pthread_t t1, pthread_t t2);
若參數t1和t2所標識的線程ID相等,則返回非零,否則返回0。
5.終止線程
① 從線程過程函數中return。
② 調用pthread_exit函數。
void pthread_exit (void* retval);
retval - 和線程過程函數的返回值語義相同。
注意:在任何線程中調用exit函數都將終止整個進程。
6.線程執行軌跡
① 同步方式(非分離狀態):
創建線程之后調用pthread_join函數等待其終止,並釋放線程資源。
② 異步方式(分離狀態):
無需創建者等待,線程終止后自行釋放資源。
int pthread_detach (pthread_t thread);
使thread參數所標識的線程進入分離(DETACHED)狀態。
處於分離狀態的線程終止后自動釋放線程資源,且不能被pthread_join函數等待。
成功返回0,失敗返回錯誤碼。
7.取消線程
① 向指定線程發送取消請求
int pthread_cancel (pthread_t thread);
成功返回0,失敗返回錯誤碼。
注意:該函數只是向線程發出取消請求,並不等待線程終止。
缺省情況下,線程在收到取消請求以后,並不會立即終止,而是仍繼續運行,直到其達到某個取消點。
在取消點處,線程檢查其自身是否已被取消了,並做出相應動作。
當線程調用一些特定函數時,取消點會出現。
② 設置調用線程的可取消狀態
int pthread_setcancelstate (int state,int* oldstate);
成功返回0,並通過oldstate參數輸出原可取消狀態(若非NULL),失敗返回錯誤碼。
state取值:
PTHREAD_CANCEL_ENABLE - 接受取消請求(缺省)。
PTHREAD_CANCEL_DISABLE - 忽略取消請求。
③ 設置調用線程的可取消類型
int pthread_setcanceltype (int type, int* oldtype);
成功返回0,並通過oldtype參數輸出原可取消類型,(若非NULL),失敗返回錯誤碼。
type取值:
PTHREAD_CANCEL_DEFERRED - 延遲取消(缺省)。
被取消線程在接收到取消請求之后並不立即響應,
而是一直等到執行了特定的函數(取消點)之后再響應該請求。
PTHREAD_CANCEL_ASYNCHRONOUS - 異步取消。
被取消線程可以在任意時間取消,不是非得遇到取消點才能被取消。
但是操作系統並不能保證這一點。
線程同步
一、互斥鎖(mutex)
通過鎖機制實現線程間的同步。同一時刻只允許一個線程執行一個關鍵部分的代碼。
1 int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr); 2 int pthread_mutex_lock(pthread_mutex_t* mutex); 3 int pthread_mutex_destroy(pthread_mutex_t * mutex); 4 int pthread_mutex_unlock(pthread_mutex_t * mutex);
① 互斥量被初始化為非鎖定狀態
② 線程1調用pthread_mutex_lock函數,立即返回,互斥量呈鎖定狀態;
③ 線程2調用pthread_mutex_lock函數,阻塞等待;
④ 線程1調用pthread_mutex_unlock函數,互斥量呈非鎖定狀態;
⑤ 線程2被喚醒,從pthread_mutex_lock函數中返回,互斥量呈鎖定狀態
特點:
互斥鎖是用在多線程多任務互斥的,一個線程占用了某一個資源,那么別的線程就無法訪問,
直到這個線程unlock,其他的線程才開始可以利用這 個資源。
二、信號量(sem)
如同進程一樣,線程也可以通過信號量來實現通信,雖然是輕量級的。
信號量是一個計數器,用於控制訪問有限共享資源的線程數。
1 // 創建信號量 2 int sem_init (sem_t* sem, int pshared,unsigned int value); 3 sem - 信號量ID,輸出。 4 pshared - 一般取0,表示調用進程的信號量。 5 非0表示該信號量可以共享內存的方式, 6 為多個進程所共享(Linux暫不支持)。 7 value - 信號量初值。 8 9 // 信號量減1,不夠減即阻塞 10 int sem_wait (sem_t* sem); 11 12 // 信號量減1,不夠減即返回-1,errno為EAGAIN 13 int sem_trywait (sem_t* sem); 14 15 // 信號量減1,不夠減即阻塞, 16 // 直到abs_timeout超時返回-1,errno為ETIMEDOUT 17 int sem_timedwait (sem_t* sem, 18 const struct timespec* abs_timeout); 19 20 struct timespec { 21 time_t tv_sec; // Seconds 22 long tv_nsec; // Nanoseconds [0 - 999999999] 23 }; 24 25 // 信號量加1 26 int sem_post (sem_t* sem); 27 28 // 銷毀信號量 29 int sem_destroy (sem_t* sem);
特點:
信號量用在多線程多任務同步的,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作
(大家都在semtake的時候,就阻塞在哪里)
三、條件變量(cond)
條件變量可以讓調用線程在滿足特定條件的情況下暫停。
int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr); //亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 使調用線程睡入條件變量cond,同時釋放互斥鎖mutex 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); struct timespec { time_t tv_sec; // Seconds long tv_nsec; // Nanoseconds [0 - 999999999] }; // 從條件變量cond中喚出一個線程, // 令其重新獲得原先的互斥鎖 int pthread_cond_signal (pthread_cond_t* cond); 注意:被喚出的線程此刻將從pthread_cond_wait函數中返回, 但如果該線程無法獲得原先的鎖,則會繼續阻塞在加鎖上。 // 從條件變量cond中喚出所有線程 int pthread_cond_broadcast (pthread_cond_t* cond); int pthread_cond_destroy (pthread_cond_t* cond);
四、