1.1為什麼要引入多線程?
從多個角度去分析下
- A.從用戶角度分析,有得程序員需要寫一個經常阻塞(比如I/O的程序),沒人希望自己程序阻塞一次就掛起。
- B.從系統設計角度分析,許多系統搞出來的線程切換的速度都是遠遠大於進程切換速度的。而且線程切換好處是Cache裡面的數據可以不拋棄,而進程切換後必須拋棄Cache裡的數據,呃,總之是快…具體的差別請看下面
- C.從硬件上分析,多CPU環境下多線程是具有優勢的。呃,這裡我保留意見,因為CPU親和力的存在使得進程也好線程也好都是盡可能在一個CPU上調度的,當然linux 有個叫CPU親和力的東西
並且有兩個設置親和力函數
int sched_setaffinity(pid_t pid, size_t cpusetsize,
cpu_set_t *mask);//進程CPU親和力
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
const cpu_set_t *cpuset);//線程CPU親和力
根據我分析代碼,pthread_setaffinity_np函數是綁定一個線程到cpuset這個參數表示的N個CPU上.這一個線程可以綁定到第0個、第1個……或第N個CPU上.我理解的對嗎
從用戶角度分析的舉例
- A.服務器程序
- B.備份程序
- C.後台文本插入程序
以上的都是為了防止阻塞而引入的,很多時候,他們是為了友好的界面設計,比如文本編輯器。由於用戶無法忍受在輸入文件時候不能用鼠標,所以才有一個線程專門用於與用戶交互;用戶也無法忍受修改了一小小的文件內容,然後卡了半天,所以才有一個線程專門在後台專門瘋狂計算;再比如用戶無法忍受輸入半天內容因為斷電而徹底沒了,所以才有一個專門定時備份的線程
現在考慮如何勾搭一個Web服務器。
A.考慮存在多線程技術的情況下,使用Reactor設計模式,主線程負責處理鏈接和分派工作。工作線程負責讀寫交互。
對應的偽代碼:
B.考慮不存在多線程技術的情況下。
- 設計一:只有一個主線程負責所有的工作,包括數據發送,每次只能處理一次鏈接
- 設計二:使用read的非阻塞版本,每次調動之前看看是否請求的東西已緩存好,如果不是,那麼就使用一個非阻塞的磁盤讀取系統調用。然後繼續循環,這個過程中,每次計算都產生一個狀態被保存,存在一個狀態不斷變化的集合,實際上我們是在模擬了多線程,但是這個過程給編程帶來極度大的困難。而這個模型稱之為有限狀態機模型。
下面是三種模型的比較
1.2經典線程模型
矛盾:線程共享同一個地址空間,意味著線程可以修改其他線程地址空間上的函數堆棧(實際上這個是很難的,因為線程是同一個用戶,而不是來自其他不同用戶)
與進程的同一特性:具有自己的堆棧,為什麼不共享堆棧?因為它們每個線程都得有自己的執行過程,如果使用同一堆棧,就極度難釋放和開拓新的函數棧幀了。
進程與線程的內容
多線程中fork下場:在Linux中,fork的時候只復制當前線程到子進程,在 fork(2)-Linux Man Page中有著這樣一段相關的描述:
The child process is created with a single thread–the one that called fork (). The entire virtual address space of the parent is replicated in the child, including the states of mutexes, condition variables, and other pthreads objects; the use of pthread_atfork(3) may be helpful for dealing with problems that this can cause.
也就是說除了調用fork的線程外,其他線程在子進程中“蒸發”了。
這就是多線程中fork所帶來的一切問題的根源所在了。之所以要這樣處理,是因為太多要處理的東西了,如果連線程都拷貝,假設有個線程專門用來蹲輸入的,如果你把這個線程也拷貝了一份,那麼最後你的輸入是給兩個人還是只給一個人??還有一個原因是共享數據結構,假設兩個線程共享一些數據結構,一個修改了另外一個怎麼處理,,,,太蛋疼了,所以linux系統干脆采取策略只拷貝調動fork的線程的狀態,但是這樣也會帶來一系列的問題,比如這個線程的鎖還在其他線程手中呢……………子進程只拷貝了帶上枷鎖的線程,而持有鎖的線程卻沒有拷貝到。
那麼如果fork後再pthread_create呢?、
1.3線程API接口
其中倒數第三個pthread_yield特別重要,因為很多用戶空間模擬出來的線程是對內核空間不可見的,一個線程執行了一段時間後可以自己主動調用這個函數來釋放CPU所有權,讓其他線程跑。