其實從著名的 C10K 問題的時候, 就談到了高并發(fā)編程時, 采用多線程(或進程)是一種不可取的解決方案, 核心原因是因為線程(或進程)本質(zhì)上都是操作系統(tǒng)的資源, 每個線程需要額外占用1M或者2M的內(nèi)存空間, 所以2G內(nèi)存,能承受的線程數(shù)差不多只能到1k這個量級。
而且線程的調(diào)度由操作系統(tǒng)調(diào)度, 當線程或者進程數(shù)到達一定量級的時候, 據(jù)有人試驗的結果是并發(fā)的線程數(shù)到達1k以上后, 操作系統(tǒng)基本上就已經(jīng)不堪重負,調(diào)度不過來了。
事件驅(qū)動
已知多線程已經(jīng)無法解決高并發(fā)問題, 所以才有了異步IO,事件驅(qū)動等概念來解決高并發(fā)編程。 很典型的就是 Node.js ,傳說中的事件驅(qū)動, 其實就是在底層使用了 libuv 然后通過各種回調(diào)函數(shù)來注冊事件, 當事件觸發(fā)的時候回調(diào)函數(shù)也被觸發(fā)。 使用事件驅(qū)動的方式確實能解決高并發(fā)的問題, 但是因為事件驅(qū)動最費勁的就是各種喪心病狂不停的回調(diào), 在回調(diào)里面再嵌套回調(diào),容易陷入所謂的【回調(diào)地獄】。 這也是 Node.js 最受人詬病的地方。
高并發(fā)解決方案之協(xié)程
面向?qū)ο笞畹湫偷恼Z言是 Java , 事件驅(qū)動最典型的語言是 Node.js , 協(xié)程最典型的語言就是 golang , 當然國內(nèi)程序員響馬的 fibjs 也是基于協(xié)程來進行并發(fā)的, 也非常出色,只不過在生態(tài)上還是發(fā)展的太慢了。 個人看來協(xié)程相對于事件驅(qū)動是一種更先進的高并發(fā)解決方案, 把復雜的邏輯和異步都封裝在底層, 讓程序員在編程時感覺不到異步的存在, 用響馬的話就是【用同步抒寫異步情懷】。 個人很看好協(xié)程的發(fā)展, 同時也非??春?golang 的前景,
協(xié)程也叫用戶級線程, 很多人分不清楚協(xié)程和線程和進程的關系。 簡單的說就是: 線程和進程的調(diào)度是由操作系統(tǒng)來調(diào)控, 而協(xié)程的調(diào)度由用戶自己調(diào)控。 所以協(xié)程調(diào)度器可以在協(xié)程A即將進入阻塞IO操作, 比如 socket 的 read (其實已經(jīng)設置為異步IO )之前, 將該協(xié)程掛起,把當前的棧信息 StackA 保存下來, 然后切換到協(xié)程B, 等到協(xié)程A的該 IO操作返回時, 再根據(jù) StackA 切回到之前的協(xié)程A當時的狀態(tài)。
小結
協(xié)程誕生解決的是低速IO和高速的CPU的協(xié)調(diào)問題,解決這類問題主要有三個有效途徑:
1.異步非阻塞網(wǎng)絡編程(libevent、libev、redis、Nginx、memcached這類)
2.協(xié)程(golang、gevent)
3.“輕量級線程”,相當于是在語言層面做抽象(Erlang)
對比之下協(xié)程的編程難度較低,不要求編程人員要有那么高的抽象思維能力。再加上golang在這方面優(yōu)秀的實踐,協(xié)程目前的前途還是一片光明的。當然還有一點,我們要承認無論你狀態(tài)機、callback設計得多么精妙,現(xiàn)實中阻塞事很難以避免的。
避免了Network IO Blocking,還有數(shù)據(jù)庫Blocking,還有Disk IO Blocking,還有數(shù)據(jù)庫Blocking,還有日志Blocking,還有第三方庫blocking,還有人類自己導致的blocking……協(xié)程是應對這些的不錯的解決方案,當然協(xié)程的接口還是太過晦澀。So,Life is Short,Use Golang。線程還是更適合作為多核計算的不二法門存在的。
原文鏈接:https://studygolang.com/articles/2396