php7协程通信使用,PHP7下的协程实现
原標(biāo)題:PHP7下的協(xié)程實(shí)現(xiàn)
什么是協(xié)程
先搞清楚,什么是協(xié)程。
你可能已經(jīng)聽過『進(jìn)程』和『線程』這兩個(gè)概念。
進(jìn)程就是二進(jìn)制可執(zhí)行文件在計(jì)算機(jī)內(nèi)存里的一個(gè)運(yùn)行實(shí)例,就好比你的.exe文件是個(gè)類,進(jìn)程就是new出來的那個(gè)實(shí)例。
進(jìn)程是計(jì)算機(jī)系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位(調(diào)度單位這里別糾結(jié)線程進(jìn)程的),每個(gè)CPU下同一時(shí)刻只能處理一個(gè)進(jìn)程。
所謂的并行,只不過是看起來并行,CPU事實(shí)上在用很快的速度切換不同的進(jìn)程。
進(jìn)程的切換需要進(jìn)行系統(tǒng)調(diào)用,CPU要保存當(dāng)前進(jìn)程的各個(gè)信息,同時(shí)還會(huì)使CPUCache被廢掉。
所以進(jìn)程切換不到非不得已就不做。
那么怎么實(shí)現(xiàn)『進(jìn)程切換不到非不得已就不做』呢?
首先進(jìn)程被切換的條件是:進(jìn)程執(zhí)行完畢、分配給進(jìn)程的CPU時(shí)間片結(jié)束,系統(tǒng)發(fā)生中斷需要處理,或者進(jìn)程等待必要的資源(進(jìn)程阻塞)等。你想下,前面幾種情況自然沒有什么話可說,但是如果是在阻塞等待,是不是就浪費(fèi)了。
其實(shí)阻塞的話我們的程序還有其他可執(zhí)行的地方可以執(zhí)行,不一定要傻傻的等!
所以就有了線程。
線程簡單理解就是一個(gè)『微進(jìn)程』,專門跑一個(gè)函數(shù)(邏輯流)。
所以我們就可以在編寫程序的過程中將可以同時(shí)運(yùn)行的函數(shù)用線程來體現(xiàn)了。
線程有兩種類型,一種是由內(nèi)核來管理和調(diào)度。
我們說,只要涉及需要內(nèi)核參與管理調(diào)度的,代價(jià)都是很大的。這種線程其實(shí)也就解決了當(dāng)一個(gè)進(jìn)程中,某個(gè)正在執(zhí)行的線程遇到阻塞,我們可以調(diào)度另外一個(gè)可運(yùn)行的線程來跑,但是還是在同一個(gè)進(jìn)程里,所以沒有了進(jìn)程切換。
還有另外一種線程,他的調(diào)度是由程序員自己寫程序來管理的,對內(nèi)核來說不可見。這種線程叫做『用戶空間線程』。
協(xié)程可以理解就是一種用戶空間線程。
協(xié)程,有幾個(gè)特點(diǎn):
協(xié)同,因?yàn)槭怯沙绦騿T自己寫的調(diào)度策略,其通過協(xié)作而不是搶占來進(jìn)行切換
在用戶態(tài)完成創(chuàng)建,切換和銷毀
?? 從編程角度上看,協(xié)程的思想本質(zhì)上就是控制流的主動(dòng)讓出(yield)和恢復(fù)(resume)機(jī)制
generator經(jīng)常用來實(shí)現(xiàn)協(xié)程
說到這里,你應(yīng)該明白協(xié)程的基本概念了吧?
PHP實(shí)現(xiàn)協(xié)程
一步一步來,從解釋概念說起!
可迭代對象
PHP5提供了一種定義對象的方法使其可以通過單元列表來遍歷,例如用foreach語句。
你如果要實(shí)現(xiàn)一個(gè)可迭代對象,你就要實(shí)現(xiàn)Iterator接口:
生成器
可以說之前為了擁有一個(gè)能夠被foreach遍歷的對象,你不得不去實(shí)現(xiàn)一堆的方法,yield關(guān)鍵字就是為了簡化這個(gè)過程。
生成器提供了一種更容易的方法來實(shí)現(xiàn)簡單的對象迭代,相比較定義類實(shí)現(xiàn)Iterator接口的方式,性能開銷和復(fù)雜性大大降低。
記住,一個(gè)函數(shù)中如果用了yield,他就是一個(gè)生成器,直接調(diào)用他是沒有用的,不能等同于一個(gè)函數(shù)那樣去執(zhí)行!
所以,yield就是yield,下次誰再說yield是協(xié)程,我肯定把你xxxx。
PHP協(xié)程
前面介紹協(xié)程的時(shí)候說了,協(xié)程需要程序員自己去編寫調(diào)度機(jī)制,下面我們來看這個(gè)機(jī)制怎么寫。
0)生成器正確使用
既然生成器不能像函數(shù)一樣直接調(diào)用,那么怎么才能調(diào)用呢?
方法如下:
foreach他
send($value)
current / next...1)Task實(shí)現(xiàn)
Task就是一個(gè)任務(wù)的抽象,剛剛我們說了協(xié)程就是用戶空間協(xié)程,線程可以理解就是跑一個(gè)函數(shù)。
所以Task的構(gòu)造函數(shù)中就是接收一個(gè)閉包函數(shù),我們命名為coroutine。
2)Scheduler實(shí)現(xiàn)
接下來就是Scheduler這個(gè)重點(diǎn)核心部分,他扮演著調(diào)度員的角色。
這樣我們基本就實(shí)現(xiàn)了一個(gè)協(xié)程調(diào)度器。
你可以使用下面的代碼來測試:
關(guān)鍵說下在哪里能用得到PHP協(xié)程。
這樣就提高了程序的執(zhí)行效率。
關(guān)于『系統(tǒng)調(diào)用』的實(shí)現(xiàn),鳥哥已經(jīng)講得很明白,我這里不再說明。
3)協(xié)程堆棧
鳥哥文中還有一個(gè)協(xié)程堆棧的例子。
我們上面說過了,如果在函數(shù)中使用了yield,就不能當(dāng)做函數(shù)使用。
所以你在一個(gè)協(xié)程函數(shù)中嵌套另外一個(gè)協(xié)程函數(shù):
這里的echoTimes是執(zhí)行不了的!所以就需要協(xié)程堆棧。
不過沒關(guān)系,我們改一改我們剛剛的代碼。
把Task中的初始化方法改下,因?yàn)槲覀冊谶\(yùn)行一個(gè)Task的時(shí)候,我們要分析出他包含了哪些子協(xié)程,然后將子協(xié)程用一個(gè)堆棧保存。(C語言學(xué)的好的同學(xué)自然能理解這里,不理解的同學(xué)我建議去了解下進(jìn)程的內(nèi)存模型是怎么處理函數(shù)調(diào)用)
當(dāng)Task->run()的時(shí)候,一個(gè)循環(huán)來分析:
然后我們增加echoTime的結(jié)束標(biāo)示:
然后修改echoTimes:
Task變?yōu)?#xff1a;
這樣就實(shí)現(xiàn)了一個(gè)協(xié)程堆棧,現(xiàn)在你可以舉一反三了。
4)PHP7中yield from關(guān)鍵字
PHP7中增加了yield from,所以我們不需要自己實(shí)現(xiàn)攜程堆棧,真是太好了。
把Task的構(gòu)造函數(shù)改回去:
echoTimes函數(shù):
task1生成器:
這樣,輕松調(diào)用子協(xié)程。返回搜狐,查看更多
責(zé)任編輯:
總結(jié)
以上是生活随笔為你收集整理的php7协程通信使用,PHP7下的协程实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: back在java里面是什么意思,手机A
- 下一篇: yii2 请求外部api_[PHP] 基