多程序、多執行緒模型、非同步模型的關係
程序、執行緒、非同步是什麼
程序是程式的一次執行過程,執行中需要使用cpu,磁碟,網路等作業系統資源。並且是獨佔式的。 執行緒 是程序內部的一條執行分支,程序內的多個執行緒資源共享,可以訪問同樣的cpu、磁碟、網路等資源。程序和執行緒都可以獨立申請cpu資源。有些程式語言提供了非同步的程式設計方式,在程式碼裡可以通過非同步的方式來做到類似子執行緒的功能,其實最底層實現還是通過執行緒池來做的(見《深入淺出nodejs》)。
主執行緒程式碼的拆分與執行緒安全問題
程式中程式碼是可以序列的順序的執行完的,但有的操作什麼時候執行完不確定,比如讀取檔案、訪問網路、訪問硬體等,這些耗時操作的時間是不可控的,如果採用單程序、單執行緒的方式,會導致程式一直阻塞在那裡,沒法做別的事情。涉及到使用者互動、涉及到ui的程式,肯定不能這樣做,需要把這段耗時操作放到別的程序、執行緒、非同步程式碼中去做,在主執行緒或者主程序裡處理使用者互動。但是有的操作也不能隨意的分出去,比如涉及到同一資源操作的程式碼,分成多個執行緒會導致併發、執行緒安全問題,需要用鎖、事務等來解決,比如資料庫解決併發修改同一份資料問題的事務。
安卓中把與渲染相關的所有操作都放在主執行緒去做,子執行緒只能做一些網路請求、檔案讀取等一些耗時的與介面無關的操作,就是為了避免併發修改的問題。H5的web worker也是一樣,只能主執行緒操作dom。通過限制訪問的方式,避免了併發的各種複雜處理。
android、ios應用因為在作業系統之上,可以直接用作業系統提供的程序、執行緒的功能。而js執行在js vm之上,前端程式碼更是執行在瀏覽器的沙箱裡,處理H5提供的Web Worker外,在應用層面涉及不到執行緒的處理。
服務端的多執行緒模型、非同步模型和多程序架構
服務端都是多執行緒的,每個請求一個執行緒來處理,比如servlet容器,他會每個請求建立一個執行緒,然後傳給具體的servelt,這也簡化了javaweb的開發,不需要在應用層面去處理執行緒的分配和釋放,應用層面的程式碼都是同步的。當然涉及到高併發的場景,靠tomcat的執行緒管理是不夠的,我們需要去了解concurrent併發包裡的api去手動處理。
都說服務端的執行緒模型受cpu的執行緒數上限的限制,而非同步不會,其實不是這樣的,非同步也是用執行緒池實現的,只不過多執行緒模型是耗時操作一直阻塞的佔用著執行緒,而非同步是動態的申請和釋放執行緒,通過把上下文獨立於執行緒來避免了執行緒資源的浪費,非同步操作結束,把結果返回主執行緒。
node的 server並不像java的tomcat容器那樣做了執行緒的管理,更何況js的非同步根本不需要我們在應用層面去操作執行緒,語言層面已經封裝了。應用層面的單執行緒會有異常處理,資源衝突等問題,可以結合多程序來擴充套件,像eggjs就是封裝了多程序的模型,結合程序和非同步可以開發企業級的應用。
程序、執行緒、非同步的關係如圖所示:

總結
因為一些應用伺服器的封裝,多執行緒模型可以在開發過程中感受不到多執行緒的處理,而非同步模型需要在應用層處理非同步。但這沒本質區別。只是一個封裝在了語言層面,一個封裝在了伺服器層面。非同步的方式確實比多執行緒的方式對執行緒資源利用率高。兩種模型都可以通過多程序來擴充套件。