執行緒的來龍去脈,你了解嗎?

  • 2019 年 10 月 10 日
  • 筆記

進程最近有些煩惱,整日愁眉苦臉的,拜訪記憶體的時候也有點心不在焉。

記憶體是個明眼人,開門見山的問道:「進程啊,最近遇到啥問題了?我看你最近情緒有點低落,有啥問題你就直接說出來嘛,我讓大傢伙兒來一起幫你想想辦法。」

進程嘆了口氣,說道:「唉,最近不是說 CPU 單核頻率到瓶頸了嗎?人類就用多核芯來彌補單核處理器性能的不足,咱們的 CPU 不也升級到四核了嘛。」

「是啊,這是好事啊,現在最多能並行處理 4 個進程,效率比以前高多了,這還不好嗎?」記憶體疑惑的問。

「好是好,可我每次上 CPU 運行的時候,都忍不住去想,要是單核頻率不增加,我總的運行的時間不還是沒有什麼變化嗎?以後的應用程式越來越大,越來越吃 CPU 資源,比如那些大型遊戲進程,在短時間內需要進行大量計算,靠單核撐不住怎麼辦。不談以後,就說說我自己,我也想能夠早點運行完,早點休息啊。」

tobe 註:很明顯單進程的運行時間是變小了的,不過這裡主要強調的是進程佔用 CPU 的時間。

記憶體點點頭,贊同道:「這個問題我倒是沒想到,多核處理器對單個進程確實不大友好。那咱得想辦法讓你能夠同時使用幾個核心。不過我一時間也想不到什麼好辦法,還是和大家一起討論下吧。」

在討論會上,記憶體向大家說明了進程現在遇到的問題。

「一個進程怎麼並行?」進程調度器第一個發出疑問:「我總不能把一個進程放在四個核上吧,這樣不僅毫無意義,還阻礙了其他進程的執行。」

作業系統見多識廣,說:「把進程一次放在幾個核上運行肯定是不可能的,我在想,咱們的目標,其實就是讓多個核心不衝突地幫助一個進程運行嘛。那我們就得把進程「拆開」,然後放在幾個核上。」

作業系統一邊說,一邊畫了張圖:

進程拆分

「你們看,假如說 fun1 和 fun2 這兩個函數互不關聯,我們就可以讓兩個核同時執行他們,這不就做到並行了嗎?」

「你的意思是說把一個進程拆成好幾個進程?」

作業系統搖搖頭:「不是拆成多個進程,進程切換的代價太大了,再說了,這些拆出來的函數,他們是共用一個地址空間的,天生就能夠數據共享,如果拆成進程,我們還得再考慮進程之間的通訊問題,那多麻煩。不過為了跟進程區分,就叫他們「執行緒(Thread)」吧」

進程一驚,要把自己拆成執行緒?那自己不就沒了?趕忙問道:「那我豈不是沒有存在的餘地了?」

進程調度器也慌了:「要是沒了進程,我是不是也要被退休了?」

作業系統趕忙解釋道:你們誤會了,我要拆開的,是進程的執行流,進程不是包含了資源所有權執行流嗎,資源所有權還是由進程來把控,執行流就分給幾個執行緒,就像這樣:

執行流

tobe 註:在進程模型里,進程擁有對記憶體、I/O 通道、I/O 設備和文件等資源的控制權,稱之為「資源所有權」。「執行流」可以看做進程在 CPU 上的執行過程(直觀一點就是高級語言里的語句)。

進程恍然大悟:「也就是說我仍然是資源的掌控者,那些執行緒就相當於幫我幹活的小弟?」

「沒錯,而且從這種角度看,你本身還是一個單執行緒進程。」

聽了這麼久,記憶體發問了:「創建進程的時候,我要保存進程 PCB ,那為了創建執行緒,我是不是還得創建一個 TCB(Thread Control Block)?」

「當然了,執行緒切換需要的資訊就得存在 TCB 裡面。不過你放心,TCB 要比 PCB 小得多,所以執行緒切換會比進程切換快很多。」

多執行緒進程模型

大家聽完,紛紛覺得「執行緒」這個模型完美的解決了當前的問題,說道:「要不我們現在就在作業系統里添上執行緒模型吧,早點解決進程的問題。」

但是作業系統面露難色,說:「執行緒模型只是我們的一個假想,貿然加進來的話,可能會出問題,系統崩潰可就不好了,還是要以穩定為主。。。但這個模型還是得試的,要不我們先創建一個執行緒庫,靠一個用戶級別的應用程式——執行緒調度器來管理這些執行緒吧。」

進程不解的問:「可是這樣的話,我還是被分配在一個單獨的核心上啊,即使是多執行緒,也只能在單核上運行。再說了,如果這些執行緒里有一個被阻塞,在你看來,是整個進程阻塞了,那其他執行緒,即使是就緒態,也得不到 CPU 資源。」

作業系統仔細想了下,說:「沒辦法,用戶級執行緒確實有這兩個缺點,但相比起讓內核來實現執行緒,用戶級執行緒也有他的好處——執行緒切換不需要我進行狀態轉換(從用戶態到內核態),開銷小,除此之外,執行緒庫可以有多個調度演算法,能夠為應用程式量身定做調度演算法。」

tobe 註:有一種解決執行緒阻塞的方案叫 jacketing,他可以把一個產生阻塞的系統調用轉化成一個非阻塞的系統調用,比如說,不直接調用系統級的 I/O 常式,而是讓執行緒調用應用級的 I/O jacket 常式,這個 jacket 常式會檢查 I/O 設備是否忙,如果忙的話,就不執行 I/O 操作,轉而調度其他執行緒,避免了因等待 I/O 設備而造成的進程阻塞。


用戶級執行緒很快投入使用,Linux系統中的 pthread(POSIX thread)庫可以說是大獲成功,作業系統做出了一項重大決定——支援內核級執行緒。

內核級執行緒解決了進程並行的問題,除此之外,由於內核看得到執行緒的存在,一個執行緒阻塞了,位於同一個進程中的其它執行緒仍然能夠運行。

用戶級執行緒和內核級執行緒

並行的問題解決了,進程表示自己十分開心。


希望你在看完我的文章之後有所收穫。

感謝你的閱讀,我們後會有期!

聲明:原創文章,未經授權,禁止轉載