相對於使用進程實現並發,用線程的實現更加輕量。每個線程都是獨立的邏輯流。線程是CPU上獨立調度啟動並執行最小單位,而進程是資源分派的單位。當然這是在微核心的作業系統上說的,簡言之這種作業系統的核心是只提供最基本的OS服務,更多參看點擊開啟連結
每個線程有它自己的線程上下文,包括一個唯一的線程ID(linux上實現為unsigned long),棧,棧指標,程式計數器、通用目的寄存器和條件碼,還有自己的訊號掩碼和優先順序。同一個進程裡的線程共用這個進程的整個虛擬位址空間,包括可執行檔程式文本、程式的全域記憶體、堆記憶體、棧和檔案描述符。因此,線程的環境切換比進程迅速得多,但同時也帶來了線程之間的同步問題。
幾個小Tips:
1、進程ID在整個系統中是唯一的,但線程ID只在它所屬的線程環境裡有效。
2、每個線程都有errno的副本,不用考慮其同步問題。
3、任意一個線程調用了exit, _Exit, _exit都會造成整個進程的終止。
4、由於描述字的共用性,新建立線程並不影響已開啟描述字的引用計數,因此,不要隨便close。
線程的幾個屬性
分離和可結合:如果一個線程能被其他線程收回其資源和殺死,它就是可結合的,否則就是分離的。預設情況下,線程被建立出來是可結合的,這時需要某個原有的線程調用pthread_join()來阻塞等待該進程結束並回收其資源(由於此函數只能等待一個特定線程會導致編碼複雜,Steven在UNP中可愛地吐槽了一段,當年與標準設計人員吵了一架)。而分離的線程無需其他線程等待,一旦結束,資源立即被作業系統回收,在其建立是調用pthread_detach()即可實現。
更多的屬性如繼承性,調度策略,調度 ,範圍和堆棧大小等可以參看APUE12.3
對於屬性變數pthread_attr_t可以調用pthread_attr_init設定,但要記得在建立線程後用pthread_attr_destory銷毀
進程同步常見方法
1)互斥鎖UNP26.7
2)條件變數UNP26.8。需要注意的是pthread_cond_wait()如果沒有訊號,線程會阻塞直到有訊號,但互斥量會立即釋放,反之,它返回時,互斥量再次被鎖住。
3)訊號量CSAPP12.5
上面只是簡單地羅列和筆記,線程水太深,我將繼續學習。。
慣例,上個簡單的伺服器端程式:
#include"simon_socket.h"#define SERV_PORT 12345typedef struct arg_tag{int connectfd;struct sockaddr_in addr_info;}ARG;void *start_routine(void *arg){ pthread_detach(pthread_self());process_client(((ARG*)arg) -> connectfd, &((ARG*)arg) -> addr_info);close(((ARG *)arg) -> connectfd);//free(arg);pthread_exit(NULL);}int main(){int sockfd, acfd;size_t sin_len = sizeof(struct sockaddr);ARG *arg;struct sockaddr_in client_addr;pthread_t thread;sockfd = init_tcp_psock(SERV_PORT);while (1){if ((acfd = accept_request(sockfd, (struct sockaddr *)&client_addr, &sin_len)) <= 0)continue;arg = malloc(sizeof(ARG));arg -> connectfd = acfd;arg -> addr_info = client_addr;if (pthread_create(&thread, NULL, start_routine, (void*)arg)){printf("Create thread error!\n");continue;}free(arg);}close(sockfd);return 0;}
代碼中一些調用函數的實現,移步我的github:https://github.com/simon-xia/lnp