摘要:粗暴地說(shuō)上面的過(guò)程就算是協(xié)程的基本概念。多線程和多進(jìn)程都是操作系統(tǒng)參與的調(diào)度,而協(xié)程是用戶自主實(shí)現(xiàn)的調(diào)度,協(xié)程的關(guān)鍵點(diǎn)實(shí)際上是用戶層實(shí)現(xiàn)自主調(diào)度,大概有翻身農(nóng)奴把歌唱的意思。
首先是,這是我第一次把公眾號(hào)文章復(fù)制粘貼到sf.gg來(lái)。
其次是,很久很久之前,我挖了一個(gè)yield的一個(gè)坑,自己挖的坑自己填,不然遲早會(huì)把自己埋掉。
最后是,如果想看之前那個(gè)坑,請(qǐng)發(fā)送“yield”給文章末尾的公眾號(hào),我開通了高大上的自動(dòng)回復(fù)功能,稀罕地不得了!
PS:那篇文章中在最后我犯了一個(gè)錯(cuò)誤,誤下了一個(gè)結(jié)論:foreach中不能使用send并猜測(cè)這是PHP的bug,實(shí)際上并不是,真實(shí)的原因粗暴簡(jiǎn)單的理解就是send會(huì)讓生成器繼續(xù)執(zhí)行一次導(dǎo)致。這件事情告訴我們:
除了裝逼之外,甩鍋也是有打臉風(fēng)險(xiǎn)的
那篇坑里,內(nèi)容和你能在百毒上搜索到的大多數(shù)文章都是差不多的,不過(guò)我那篇坑標(biāo)題起得好:《yield是個(gè)什么玩意(上)》,也就是暗示大家還有下篇,所以起標(biāo)題也是需要一定技術(shù)含量的。
我堅(jiān)信,在座的各位辣雞在看完上篇坑文后最想說(shuō)的注定是泰迪熊這句話(這是文化屬性,不以各位的意志而轉(zhuǎn)移):
回到今天主旨上來(lái),強(qiáng)調(diào)幾點(diǎn):
雖然文章標(biāo)題中有“yield和協(xié)程”這樣的關(guān)鍵字,但實(shí)際上yield并不是協(xié)程,看起來(lái)有不少人直接將yield和協(xié)程劃了等號(hào)。yield的本質(zhì)是生成器,英文名字叫做Generator。
yield只能用在function中,但用了yield就已經(jīng)不是傳統(tǒng)意義上的function了,同時(shí)如果你企圖在function之外的其他地方用yield,你會(huì)被打臉。
yield的最重要作用就是:自己中斷一坨代碼的執(zhí)行,然后主動(dòng)讓出CPU控制權(quán)給路人甲;然后又能通過(guò)一些方式從剛才中斷的地方恢復(fù)運(yùn)行。這個(gè)就比較屌了,假如你請(qǐng)求了一個(gè)費(fèi)時(shí)10s的服務(wù)器API,此時(shí)是可以讓出CPU給路人甲。粗暴地說(shuō)上面的過(guò)程就算是協(xié)程的基本概念。
多線程和多進(jìn)程都是操作系統(tǒng)參與的調(diào)度,而協(xié)程是用戶自主實(shí)現(xiàn)的調(diào)度,協(xié)程的關(guān)鍵點(diǎn)實(shí)際上是“用戶層實(shí)現(xiàn)自主調(diào)度”,大概有“翻身農(nóng)奴把歌唱”的意思。
下面我通過(guò)一坨代碼來(lái)體會(huì)一把“翻身農(nóng)奴”,你們感受一下:
current(); // 這會(huì)兒我可以讓task2介入進(jìn)來(lái)了 echo $task2->current(); // task1恢復(fù)中斷 $task1->next(); // task2恢復(fù)中斷 $task2->next(); }
上面代碼執(zhí)行結(jié)果如下圖:
雖然我話都說(shuō)到這里了,但是肯定還是有人get不到“所以,到底發(fā)生了什么?”。你要知道,如果function gen1和function gen2中沒(méi)有yield,而是普通函數(shù),你是無(wú)法中斷其中的for循環(huán)的,諸如下面這樣的代碼:
我似乎已然精通了yield寫到這里后我也開始蹩了,和以往的憋了三天蹦不出來(lái)個(gè)屁有所不同,我這次蹩出了一個(gè)比較典型的應(yīng)用場(chǎng)景:curl。下面我們基于上面那坨辣雞代碼將gen1修改為一個(gè)耗時(shí)curl網(wǎng)絡(luò)請(qǐng)求,gen2將向一個(gè)文本文件中寫內(nèi)容,我們的目的就是在耗時(shí)的curl開始后主動(dòng)讓出CPU,讓gen2去寫文件,以實(shí)現(xiàn)CPU的最大化利用。
0 ); $ret = curl_multi_getcontent( $ch1 ); echo $ret.PHP_EOL; return false; } function gen2() { for ( $i = 1; $i <= 10; $i++ ) { echo "gen2 : {$i}".PHP_EOL; file_put_contents( "./yield.log", "gen2".$i, FILE_APPEND ); yield; } } $gen1 = gen1( $mh, $ch1 ); $gen2 = gen2(); while( true ) { echo $gen1->current(); echo $gen2->current(); $gen1->next(); $gen2->next(); }上面的代碼,運(yùn)行以后,我們?cè)俚却齝url發(fā)起請(qǐng)求的5秒鐘內(nèi),同時(shí)可以完成文件寫入功能,如果換做平時(shí)的PHP程序,就只能是先阻塞等待curl拿到結(jié)果后才能完成文件寫入。
文章太長(zhǎng),就像“老太太的裹腳布一樣,又臭又長(zhǎng)”,所以,最后再對(duì)代碼做個(gè)極小幅度的改動(dòng)就收尾不寫了!
0 ); $ret = curl_multi_getcontent( $ch1 ); echo $ret.PHP_EOL; return false; } function gen2() { for ( $i = 1; $i <= 10; $i++ ) { echo "gen2 : {$i}".PHP_EOL; file_put_contents( "./yield.log", "gen2".$i, FILE_APPEND ); $rs = yield; echo "外部發(fā)送數(shù)據(jù){$rs}".PHP_EOL; } } $gen1 = gen1( $mh, $ch1 ); $gen2 = gen2(); while( true ) { echo $gen1->current(); echo $gen2->current(); $gen1->send("gen1"); $gen2->send("gen2"); }我們修改了內(nèi)容:
將$gen1->next()修改成了$gen1->send("gen1")
在function gen1中yield有了返回值,并且將返回值打印出來(lái)
這件事情告訴我們:yield和send,是可以雙向通信的,同時(shí)告訴我們send可以用來(lái)恢復(fù)原來(lái)中斷的代碼,而且在恢復(fù)中斷的同時(shí)可以攜帶信息回去。寫到這里,你是不是覺(jué)得這玩意的可利用價(jià)值是不是比原來(lái)高點(diǎn)兒了?
我知道,有人肯定叨叨了:“老李,你代碼特么寫的真是辣雞啊!你之前保證過(guò)了的 --- 只在公司生產(chǎn)環(huán)境寫辣雞代碼的??赡憧纯茨氵@辣雞光環(huán)到籠罩都到demo里了,你連demo都不放過(guò)了!你怎么說(shuō)?!”。兄dei,“又不是不能用”。而且我告訴你,上面這點(diǎn)兒curl demo來(lái)講明白yield還是不夠的,后面還有兩三篇yield呢,照樣是爛代碼惡心死你,愛(ài)看不看。我勸你心放寬,你想想你這么爛的代碼都經(jīng)歷了,還有什么不能經(jīng)歷的?
文章最后補(bǔ)個(gè)小故事:其實(shí)yield是PHP 5.5就已經(jīng)添加進(jìn)來(lái)了,這個(gè)模塊的作者叫做Nikita Popov,網(wǎng)絡(luò)上的名稱是Nikic。我們知道PHP7這一代主力是惠新宸,下一代PHP主力就是Nikic了。早在2012年,Nikic就發(fā)表了一篇關(guān)于PHP yield多任務(wù)的文章,鏈接我貼出來(lái)大家共賞一下 --- http://nikic.github.io/2012/1...
最近開了一個(gè)微信公眾號(hào),所有文章都在這里
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30994.html
摘要:并發(fā)用于制定方案,用來(lái)解決可能但未必并行的問(wèn)題。在協(xié)程中使用需要注意兩點(diǎn)使用鏈接的多個(gè)協(xié)程最終必須由不是協(xié)程的調(diào)用方驅(qū)動(dòng),調(diào)用方顯式或隱式在最外層委派生成器上調(diào)用函數(shù)或方法。對(duì)象可以取消取消后會(huì)在協(xié)程當(dāng)前暫停的處拋出異常。 導(dǎo)語(yǔ):本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來(lái)一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、了解asyncio...
摘要:本文先回顧生成器,然后過(guò)渡到協(xié)程編程。其作用主要體現(xiàn)在三個(gè)方面數(shù)據(jù)生成生產(chǎn)者,通過(guò)返回?cái)?shù)據(jù)數(shù)據(jù)消費(fèi)消費(fèi)者,消費(fèi)傳來(lái)的數(shù)據(jù)實(shí)現(xiàn)協(xié)程。解決回調(diào)地獄的方式主要有兩種和協(xié)程。重點(diǎn)應(yīng)當(dāng)關(guān)注控制權(quán)轉(zhuǎn)讓的時(shí)機(jī),以及協(xié)程的運(yùn)作方式。 轉(zhuǎn)載請(qǐng)注明文章出處: https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請(qǐng)求 cookie web響應(yīng) sess...
摘要:消息向迭代器獲取所表示的底層序列的下一個(gè)元素。為了對(duì)方法調(diào)用做出回應(yīng),迭代器可以執(zhí)行任何計(jì)算來(lái)獲取或計(jì)算底層數(shù)據(jù)序列的下一個(gè)元素。這個(gè)迭代器應(yīng)擁有方法,依次返回序列中的每個(gè)元素,最后到達(dá)序列末尾時(shí)產(chǎn)生異常。 第五章 序列和協(xié)程 來(lái)源:Chapter 5: Sequences and Coroutines 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 5.1 引言 在這一章中,我...
摘要:函數(shù)并不是生成器協(xié)程函數(shù)自動(dòng)執(zhí)行的唯一方案。因?yàn)樽詣?dòng)執(zhí)行的關(guān)鍵是,必須有一種機(jī)制,自動(dòng)控制生成器協(xié)程函數(shù)的流程,接收和交還程序的執(zhí)行權(quán)?;卣{(diào)函數(shù)可以做到這一點(diǎn),對(duì)象也可以做到這一點(diǎn)。本系列的下一篇,將介紹基于的實(shí)現(xiàn)的自動(dòng)執(zhí)行器。 PHP下的異步嘗試系列 如果你還不太了解PHP下的生成器和協(xié)程,你可以根據(jù)下面目錄翻閱 PHP下的異步嘗試一:初識(shí)生成器 PHP下的異步嘗試二:初識(shí)協(xié)程 P...
摘要:協(xié)程的基本行為協(xié)程包含四種狀態(tài)等待開始執(zhí)行。協(xié)程中重要的兩個(gè)方法調(diào)用方把數(shù)據(jù)提供給協(xié)程。注意使用調(diào)用協(xié)程時(shí)會(huì)自動(dòng)預(yù)激,因此與裝飾器不兼容標(biāo)準(zhǔn)庫(kù)中的裝飾器不會(huì)預(yù)激協(xié)程,因此能兼容句法。因此,終止協(xié)程的本質(zhì)在于向協(xié)程發(fā)送其無(wú)法處理的異常。 導(dǎo)語(yǔ):本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來(lái)一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、掌握協(xié)...
閱讀 1558·2021-11-18 10:02
閱讀 1728·2021-09-04 16:40
閱讀 3200·2021-09-01 10:48
閱讀 903·2019-08-30 15:55
閱讀 1883·2019-08-30 15:55
閱讀 1398·2019-08-30 13:05
閱讀 3041·2019-08-30 12:52
閱讀 1643·2019-08-30 11:24