線程的使用
每個進程都有一個地址空間和一個控制線程,但是我們經常需要在同一個地址空間里運行多個控制線程,它們協同工作完成任務,線程被需要的理由如下:
- 包含多個線程的進程配合工作的處理速度比單線程快
- 線程比進程更輕量級,創建和銷毀線程較之于進程更為高效
- 在多核系統中,多線程更能充分利用資源
比如我們日常使用網易云音樂時,如果該進程只有一個線程,音樂播放本地歌曲的時候我們就不能同時在后臺下載歌曲,搜索歌曲的時候就要暫停播放音樂,這顯然是用戶無法接受的。如果我們采用多線程編程,可以用一個線程播放音樂,一個線程在后臺默默地下載音樂,一個線程接受用戶輸入并搜索結果,用戶體驗自然非常好。
多線程編程在圖像處理和Web服務器開發方面是重頭戲。Web服務器可以在等待等待用戶請求時阻塞,每當一個請求到達便彈出一個線程處理,可以同時處理大量請求,將應答返回給用戶。
線程模型
進程是一個正在執行的程序的實例,用于把資源集中到一起,包括地址空間、打開的額文件,子進程和信號等,進程和進程之間相對獨立,而線程共享進程的資源,有著完全一樣的地址空間和全局變量,不同的是每個線程有著各自的堆棧。
線程的狀態
和進程一樣,線程有3種狀態:運行(Running),就緒(Ready),阻塞(BLocked)
- Running運行態(使用CPU中)
- Ready就緒態(可運行,等待被調度)
-
Blocked阻塞態(等待外部事件發生)
線程狀態
一個處于運行態的線程在等待用戶輸入時會發生轉換1進入阻塞態,在用戶輸入時會發生轉換2進入就緒態。轉換3和轉換4可在操作系統的調度下發生。
在用戶空間實現線程
線程可在用戶空間和內核空間實現。
在用戶空間實現線程,內核并不會知道進程內有多個線程,仍把進程當做單線程進程來對待,內核通過其內部的進程表來管理進程,而每個進程內部有一張用于供進程管理線程的線程表。用戶空間實現線程的有點是顯而易見的:
- 由于不需要陷入內核,線程的創建和銷毀完全在用戶空間,速度很快
- 每個進程可以制定自己的調度算法
但是其有點完全掩蓋不了其缺點:但其中一個線程阻塞時,同一個進程里面的所有線程都會被掛起。比如其中一個線程做I/O操作或是發生缺頁故障時(需要從磁盤載入頁面到物理內存),該線程會被阻塞,原因是內核管理的是進程。
在內核空間實現線程
現代主流的操作系統是在內核空間實現線程,如下圖
和在用戶空間實現線程不同,此時線程表移到了內核空間,由內核對所有的線程單獨管理。當一個線程阻塞時,內核可根據線程表調用同一進程中的線程或是另外一個進程中的線程,每個線程都是獨立的個體,一個線程阻塞就不會像在用戶空間中實現的線程那樣,阻塞整個進程。
當然,線程的創建和銷毀都需要執行系統調用,從用戶空間陷入內核空間,上下文切換的代價比較大。