在看swoole的時候蘸秘,看到php 定義協(xié)程的方式是
go(function () {
echo "hello wangbaojin \n";
});
此時嚇我一大跳店溢,因為之前寫過golang,go里定義協(xié)程的方式是
go func
難道是php 這里又是抄襲了 go???????抱著好奇的心情展開了調查
########################################################
各種語言的協(xié)程支持情況
C++
可以通過 Boost.Coroutine 庫實現(xiàn)協(xié)程。
Java
不支持協(xié)程
Python 3.5
加入了 async def 對協(xié)程的支持,但是Python的 協(xié)程是在方法定義時就確定了,被定義協(xié)程的方法不能當做普通方法來使用铜异,而Go語言
swoole介紹
Swoole 使用純 C 語言編寫,提供了 PHP 語言的異步多線程服務器秸架,異步 TCP/UDP 網(wǎng)絡客戶端揍庄,異步 MySQL,異步 Redis东抹,數(shù)據(jù)庫連接池蚂子,AsyncTask沃测,消息隊列,毫秒定時器食茎,異步文件讀寫蒂破,異步DNS查詢。 Swoole內置了Http/WebSocket服務器端/客戶端别渔、Http2.0服務器端附迷。
除了異步 IO 的支持之外,Swoole 為 PHP 多進程的模式設計了多個并發(fā)數(shù)據(jù)結構和IPC通信機制哎媚,可以大大簡化多進程并發(fā)編程的工作喇伯。其中包括了并發(fā)原子計數(shù)器,并發(fā) HashTable拨与,Channel艘刚,Lock,進程間通信IPC等豐富的功能特性截珍。
Swoole2.0 支持了類似 Go 語言的協(xié)程,可以使用完全同步的代碼實現(xiàn)異步程序箩朴。PHP 代碼無需額外增加任何關鍵詞岗喉,底層自動進行協(xié)程調度,實現(xiàn)異步炸庞。
swoole協(xié)程與go的協(xié)程對比
區(qū)別 | swoole協(xié)程 | go 協(xié)程 |
---|---|---|
底層原理 | 單進程 | 多線程 |
CSP理論 | 單進程 簡單 / 不用加鎖 / 性能也高 | MPG模型 |
MPG模型
CSP理論
SP模型是上個世紀七十年代提出的钱床,用于描述兩個獨立的并發(fā)實體通過共享的通訊 channel(管道)進行通信的并發(fā)模型。 CSP中channel是第一類對象埠居,它不關注發(fā)送消息的實體查牌,而關注與發(fā)送消息時使用的channel。
Golang CSP
Golang 就是借用CSP模型的一些概念為之實現(xiàn)并發(fā)進行理論支持滥壕,其實從實際上出發(fā)纸颜,go語言并沒有,完全實現(xiàn)了CSP模型的所有理論绎橘,僅僅是借用了 process和channel這兩個概念胁孙。process是在go語言上的表現(xiàn)就是 goroutine 是實際并發(fā)執(zhí)行的實體,每個實體之間是通過channel通訊來實現(xiàn)數(shù)據(jù)共享称鳞。
Channel
Golang中使用 CSP中 channel 這個概念涮较。channel 是被單獨創(chuàng)建并且可以在進程之間傳遞,它的通信模式類似于 boss-worker 模式的冈止,一個實體通過將消息發(fā)送到channel 中狂票,然后又監(jiān)聽這個 channel 的實體處理,兩個實體之間是匿名的熙暴,這個就實現(xiàn)實體中間的解耦闺属,其中 channel 是同步的一個消息被發(fā)送到 channel 中慌盯,最終是一定要被另外的實體消費掉的,在實現(xiàn)原理上其實是一個阻塞的消息隊列屋剑。
Goroutine
Goroutine 是實際并發(fā)執(zhí)行的實體润匙,它底層是使用協(xié)程(coroutine)實現(xiàn)并發(fā),coroutine是一種運行在用戶態(tài)的用戶線程唉匾,類似于 greenthread孕讳,go底層選擇使用coroutine的出發(fā)點是因為,它具有以下特點:
用戶空間 避免了內核態(tài)和用戶態(tài)的切換導致的成本
可以由語言和框架層進行調度
更小的椢”欤空間允許創(chuàng)建大量的實例
可以看到第二條 用戶空間線程的調度不是由操作系統(tǒng)來完成的厂财,像在java 1.3中使用的greenthread的是由JVM統(tǒng)一調度的(后java已經(jīng)改為內核線程),還有在ruby中的fiber(半?yún)f(xié)程) 是需要在重新中自己進行調度的峡懈,而goroutine是在golang層面提供了調度器璃饱,并且對網(wǎng)絡IO庫進行了封裝,屏蔽了復雜的細節(jié)肪康,對外提供統(tǒng)一的語法關鍵字支持荚恶,簡化了并發(fā)程序編寫的成本。
附 實現(xiàn)高并發(fā)的幾種方式
多線程
提起并發(fā)編程磷支,最常見的就是多線程編程谒撼。線程是操作系統(tǒng)能夠進行調度的最小單位,共享同一進程的數(shù)據(jù)和資源雾狈,有內核線程和用戶線程之分廓潜,由操作系統(tǒng)或用戶進程調度。多線程的程序可以利用多核CPU善榛,并行地處理多個任務辩蛋。隨著并發(fā)量增大,線程數(shù)增加移盆,多線程的并發(fā)模型面臨一些問題悼院。
內存占用
64位JVM線程默認棧空間是1M咒循,啟動1024個線程理論上消耗1G的椨8颍空間。由于線程需要內存較多剑鞍,為避免內存耗盡昨凡,應用程序不應該大量創(chuàng)建線程跪者。
線程調度
操作系統(tǒng)對線程進行調度也需要成本柄粹。線程掛起前會保存線程上下文到検锬簦空間磕道,再切換到可執(zhí)行線程选酗。線程數(shù)很多時控硼,線程上下文切換會導致CPU開銷變大诈唬。
使用線程池技術可以對多線程并發(fā)進行優(yōu)化桐罕,線程池實現(xiàn)線程復用,避免頻繁創(chuàng)建新線程和線程切換晌杰,控制了線程數(shù)量跷睦,減少了內存的消耗。但是在競爭共享數(shù)據(jù)的時候肋演,需要用加鎖來保護共享數(shù)據(jù)抑诸,這樣也降低了程序的并發(fā)效率。
異步回調
為了充分利用CPU爹殊,不讓線程空等待蜕乡,在線程阻塞的時候,注冊一個回調方法梗夸,讓當前線程不再阻塞层玲,去處理新的請求。等結果準備好反症,調度器把結果傳給回調方法辛块,在回調方法中繼續(xù)處理結果。然而回調方法并不在發(fā)起請求的線程里執(zhí)行铅碍。
異步回調的缺點是所謂的callback hell憨降。原本順序同步的執(zhí)行邏輯拆分到回調方法中,而且回調方法中可能再嵌套回調方法该酗。這種寫程序的方式還是讓很多程序員不太習慣。異步回調的典型實現(xiàn)是NodeJS士嚎,目前也有一些第三方模塊將異步代碼同步化呜魄。
協(xié)程(纖程)
協(xié)程(纖程)也是一種異步方案。在代碼IO阻塞時莱衩,當前協(xié)程讓出CPU執(zhí)行權爵嗅,讓其它協(xié)程執(zhí)行。待IO操作完畢笨蚁,阻塞的協(xié)程繼續(xù)執(zhí)行睹晒。雖然代碼是異步執(zhí)行,但寫代碼看起來像是同步的括细。協(xié)程是用戶態(tài)的輕量級線程伪很,現(xiàn)在的機器可以啟動百萬數(shù)量的協(xié)程。支持協(xié)程的編程語言實現(xiàn)了協(xié)程的調度器奋单,提供了channel機制進行協(xié)程間通信(CSP模型中消息傳遞的實現(xiàn))锉试。基于CSP模型的協(xié)程方案览濒,實現(xiàn)了無共享內存無鎖的并發(fā)呆盖,可以匹配異步回調的性能拖云。