1、如果邏輯控制流程在時間上重疊,那麼它們就是並發的。這種現象,稱為並發(concurrency)。
2、為了允許伺服器同時為大量用戶端服務,比較好的方法是:建立並發伺服器,為每個用戶端建立各自獨立的邏輯流。現代OS提供的常用構造並發的方法有:
進程和線程。
1)每個邏輯流都是一個進程,由核心來調度維護。每個進程都有獨立的虛擬位址空間,控制流程通過IPC機制來進行通訊。
2)線程:運行在單一進程上下文中的邏輯流,由核心進行調度,共用同一進程的虛擬位址空間。
由於進程式控制制和IPC的開銷較高,所以基於進程的設計比基於線程的設計慢。
常見IPC有:管道,FIFO,共用儲存空間,訊號。
3、基於線程的並發編程
線程由核心自動調度,每個線程都有它自己的線程上下文(thread context),包括一個惟一的整數線程ID(Thread ID,TID),棧,棧指標,程式計數器,通用目的寄存器和條件碼。每個線程和其他線程一起共用進程內容相關的剩餘部分,包括整個使用者的虛擬位址空間,它是由唯讀文本(代碼),讀/寫資料,堆以及所有的共用庫代碼和資料區域組成的,還有,線程也共用同樣的開啟檔案的集合。
1)線程執行模型
線程不像進程那樣,不是按照嚴格的父子層次來組織的。和一個進程相關的線程組成一個對等線程池(a pool of peers),獨立於其他線程建立的線程(The threads associated with a process form a pool of peers, independent of which threads were created by which other threads.個人理解,這句是說獨立於其他進程的線程池中的線程)。進程中第一個啟動並執行線程稱為主線程。對等(線程)池概念的主要影響是,一個線程可以殺死它的任何對等線程,或者等待它的任意對等線程終止;進一步來說,每個對等線程都能讀寫相同的共用資料。
2)關於posix線程樣本,及其相關函數,參見原書13.3.2節中。這部分舉的例子很經典,值得一讀
3)分離線程
在任何一個時間點上,線程是可結合的(joinable),或者是分離的(detached)。一個可結合的線程能夠被其他線程收回其資源和殺死。在被其他線程回收之前,它的儲存空間資源(如棧)是不釋放的。相反,一個分離的線程是不能被其他線程回收或殺死的,它的儲存空間資源在它終止時由系統自動釋放。
樣本程式
/* * echoservert.c - A concurrent echo server using threads *//* $begin echoservertmain */#include "csapp.h"void echo(int connfd);void *thread(void *vargp);int main(int argc, char **argv) { int listenfd, *connfdp, port, clientlen=sizeof(struct sockaddr_in); struct sockaddr_in clientaddr; pthread_t tid; if (argc != 2) {fprintf(stderr, "usage: %s <port>\n", argv[0]);exit(0); } port = atoi(argv[1]); listenfd = Open_listenfd(port); while (1) {connfdp = Malloc(sizeof(int));*connfdp = Accept(listenfd, (SA *) &clientaddr, &clientlen);Pthread_create(&tid, NULL, thread, connfdp); }}/* thread routine */void *thread(void *vargp) { int connfd = *((int *)vargp); Pthread_detach(pthread_self()); Free(vargp); echo(connfd); Close(connfd); return NULL;}/* $end echoservertmain */