国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PHP回顧之協(xié)程

Java3y / 884人閱讀

摘要:本文先回顧生成器,然后過渡到協(xié)程編程。其作用主要體現(xiàn)在三個(gè)方面數(shù)據(jù)生成生產(chǎn)者,通過返回?cái)?shù)據(jù)數(shù)據(jù)消費(fèi)消費(fèi)者,消費(fè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)

session

數(shù)據(jù)庫(kù)操作

加解密

Composer

創(chuàng)建自己的Composer包

發(fā)送郵件

IO

Socket編程

多進(jìn)程編程

執(zhí)行流程及相關(guān)概念

PHP自5.5起引入了生成器(Generator),基于其可實(shí)現(xiàn)協(xié)程編程。本文先回顧生成器,然后過渡到協(xié)程編程。

yield與生成器 生成器

生成器是一種數(shù)據(jù)類型,實(shí)現(xiàn)了iterator接口。不能通過new得到生成器實(shí)例,也沒有獲取生成器實(shí)例的靜態(tài)方法。得到生成器實(shí)例的唯一辦法是調(diào)用生成器函數(shù)(包含yield關(guān)鍵字的函數(shù))。調(diào)用生成器函數(shù)直接返回一個(gè)生成器對(duì)象,生成器運(yùn)行時(shí)函數(shù)內(nèi)的代碼才開始執(zhí)行。

先上代碼直觀感受一下yield與生成器:

# generator1.php
function foo() {
    exit("exit script when generator runs.");
    yield;
}

$gen = foo();
var_dump($gen);
$gen->current();

echo "unreachable code!";

# 執(zhí)行結(jié)果
object(Generator)#1 (0) {
}
exit script when generator runs.

foo函數(shù)包含yield關(guān)鍵字,變身為生成器函數(shù)。調(diào)用foo不會(huì)執(zhí)行函數(shù)體中的任何代碼,而是返回一個(gè)生成器實(shí)例。生成器運(yùn)行后,foo函數(shù)內(nèi)的代碼執(zhí)行,腳本結(jié)束。

如其名,生成器可以用來生成數(shù)據(jù)。只是其生成數(shù)據(jù)的方式與其他函數(shù)不一樣:生成器通過yield返回?cái)?shù)據(jù),而非return; yield返回?cái)?shù)據(jù)后,生成器函數(shù)不會(huì)銷毀,只是暫停運(yùn)行,未來可以從暫停處恢復(fù)運(yùn)行;生成器運(yùn)行一次,(只)返回一個(gè)數(shù)據(jù),多次運(yùn)行就返回多個(gè)數(shù)據(jù);不調(diào)用生成器獲取數(shù)據(jù),生成器內(nèi)的代碼就躺著不動(dòng),所謂動(dòng)次打次,說的就是生成器生成數(shù)據(jù)的樣子。

生成器實(shí)現(xiàn)了迭代器接口,獲取生成器數(shù)據(jù)可以用foreach循環(huán)或手工current/next/valid。如下代碼演示數(shù)據(jù)生成和遍歷:

# generator2.php
function foo() {
  # 返回鍵值對(duì)數(shù)據(jù)
  yield "key1" => "value1";
  $count = 0;
  while ($count < 5) {
    # 返回值,key自動(dòng)生成
    yield $count;
    ++ $count;
  }
  # 不返回值,相當(dāng)于返回null
  yield;
}

# 手動(dòng)獲取生成器數(shù)據(jù)
$gen = foo();
while ($gen->valid()) {
  fwrite(STDOUT, "key:{$gen->key()}, value:{$gen->current()}
");
  $gen->next();
}

# foreach 遍歷數(shù)據(jù)
fwrite(STDOUT, "
data from foreach
");
foreach (foo() as $key => $value) {
    fwrite(STDOUT, "key:$key, value:$value
");
}
yield

yield關(guān)鍵字是生成器的核心,其讓普通函數(shù)異化(進(jìn)化)為生成器函數(shù)。yield有“讓出”的意思,程序執(zhí)行到yield語句會(huì)暫停執(zhí)行,讓出CPU并將控制權(quán)返回到調(diào)用者,下次執(zhí)行時(shí)從中斷點(diǎn)繼續(xù)執(zhí)行。控制權(quán)返回到調(diào)用者時(shí),yield語句可以攜帶值返回給調(diào)用方。generator2.php腳本演示了yield返回值的三種形式:

yield $key => $value: 返回?cái)?shù)據(jù)的key和value;

yield $value: 返回?cái)?shù)據(jù),key由系統(tǒng)分配;

yield: 返回null值,key由系統(tǒng)分配;

yield讓函數(shù)可以隨時(shí)暫停、繼續(xù)執(zhí)行,并返回?cái)?shù)據(jù)給調(diào)用方。如果繼續(xù)執(zhí)行時(shí)需要外部數(shù)據(jù),這個(gè)工作由生成器的send函數(shù)提供:出現(xiàn)在yield左邊等號(hào)的變量會(huì)接收send傳來的值。看一個(gè)常見的send函數(shù)使用樣例:

function logger(string $filename) {
  $fd = fopen($filename, "w+");
  while($msg = yield) {
    fwrite($fd, date("Y-m-d H:i:s") . ":" . $msg . PHP_EOL);
  }
  fclose($fd);
}

$logger = logger("log.txt");
$logger->send("program starts!");
// do some thing
$logger->send("program ends!");

send讓生成器之間和外部有雙向數(shù)據(jù)通信的能力:yield返回?cái)?shù)據(jù);send提供繼續(xù)運(yùn)行的支撐數(shù)據(jù)。由于send讓生成器繼續(xù)執(zhí)行,這個(gè)行為與迭代器的next接口類似,next相當(dāng)于send(null)

其他

$string = yield $data;的表達(dá)式在PHP7前不合法,需要加括號(hào):$string = (yield $data);

PHP5生成器函數(shù)不能return值,PHP7后可以return值,并通過生成器的getReturn獲取返回的值。詳情參考返回值的RFC:https://wiki.php.net/rfc/gene...;

PHP7新增了yield from語法,實(shí)現(xiàn)了生成器委托,詳情請(qǐng)參考其RFC: https://wiki.php.net/rfc/gene...;

生成器是單向迭代器,開動(dòng)后不能調(diào)用rewind

總結(jié)

相對(duì)于其他迭代器,生成器具有性能開銷小、編碼容易的特點(diǎn)。其作用主要體現(xiàn)在三個(gè)方面:

數(shù)據(jù)生成(生產(chǎn)者),通過yield返回?cái)?shù)據(jù);

數(shù)據(jù)消費(fèi)(消費(fèi)者),消費(fèi)send傳來的數(shù)據(jù);

實(shí)現(xiàn)協(xié)程。

關(guān)于PHP中的生成器及基本用法,建議看看 2gua 大佬的博文:PHP之生成器,生動(dòng)有趣且易懂。

協(xié)程編程

協(xié)程(coroutine)是隨時(shí)可中斷、恢復(fù)執(zhí)行的子程序,yield關(guān)鍵字讓函數(shù)擁有這種能力,所以可以用于協(xié)程編程。

進(jìn)程、線程和協(xié)程

線程歸屬于進(jìn)程,一個(gè)進(jìn)程可有多個(gè)線程。進(jìn)程是計(jì)算機(jī)分配資源的最小單位,線程是計(jì)算機(jī)調(diào)度執(zhí)行的最小單位。進(jìn)程和線程均由操作系統(tǒng)調(diào)度。

協(xié)程可以看成“用戶態(tài)的線程”,需要用戶程序?qū)崿F(xiàn)調(diào)度。線程和進(jìn)程由操作系統(tǒng)調(diào)度“搶占式”交替運(yùn)行,協(xié)程主動(dòng)讓出CPU“協(xié)商式”交替運(yùn)行。協(xié)程十分的輕量,協(xié)程切換不涉及線程切換,執(zhí)行效率高,數(shù)目越多,越能體現(xiàn)協(xié)程的優(yōu)勢(shì)。

生成器和協(xié)程

生成器實(shí)現(xiàn)的協(xié)程屬于無棧協(xié)程(stackless coroutine),即生成器函數(shù)只有函數(shù)幀,運(yùn)行時(shí)附加到調(diào)用方的棧上執(zhí)行。不同于功能強(qiáng)大的有棧協(xié)程(stackful coroutine),生成器暫停后無法控制程序走向,只能將控制權(quán)被動(dòng)的歸還調(diào)用者;生成器只能中斷自身,不能中斷整個(gè)協(xié)程。當(dāng)然,生成器的好處便是效率高(暫停時(shí)只需保存程序計(jì)數(shù)器即可),實(shí)現(xiàn)簡(jiǎn)單。

協(xié)程編程

說到PHP中的協(xié)程編程,相信大部分人已經(jīng)看過鳥哥轉(zhuǎn)載(翻譯)的這篇博文:在PHP中使用協(xié)程實(shí)現(xiàn)多任務(wù)調(diào)度。原文作者 nikic 是PHP的核心開發(fā)者,生成器功能的倡議者和實(shí)現(xiàn)人。想深入了解生成器及基于其的協(xié)程編程,nikic關(guān)于生成器的RFC和鳥哥網(wǎng)站上的文章必讀。

nikic的文章,生成器部分好懂,看完后用yield寫個(gè)xrange類似函數(shù)肯定毫無壓力。為什么一進(jìn)入?yún)f(xié)程,就有點(diǎn)懵逼呢?

先看看基于生成器的協(xié)程工作方式:協(xié)程協(xié)作式工作,即協(xié)程之間通過主動(dòng)讓出CPU達(dá)到多任務(wù)交替運(yùn)行(即并發(fā)多任務(wù),但不是并行);一個(gè)生成器可看成一個(gè)協(xié)程,執(zhí)行到yield語句,讓出CPU控制權(quán)回到調(diào)用方,調(diào)用方繼續(xù)執(zhí)行其他協(xié)程或其他代碼。

再來看鳥哥博客理解的難點(diǎn)何在。協(xié)程非常輕量,一個(gè)系統(tǒng)中可以同時(shí)存在成千上萬個(gè)協(xié)程(生成器)。而操作系統(tǒng)不會(huì)對(duì)協(xié)程調(diào)度,安排協(xié)程執(zhí)行的工作就落到開發(fā)者身上。部分人看不懂鳥哥文章的協(xié)程部分,是因?yàn)槔锩嬲f協(xié)程編程少(寫協(xié)程主要就是寫生成器函數(shù)),而是花筆墨實(shí)現(xiàn)了一個(gè)協(xié)程的調(diào)度器(scheduler或者kernel):模擬了操作系統(tǒng),對(duì)所有協(xié)程進(jìn)行公平調(diào)度。PHP開發(fā)一般的思維是:我寫了這些代碼,PHP引擎會(huì)調(diào)用我這些代碼得到預(yù)期結(jié)果。而協(xié)程編程不僅要寫干活的代碼,還要寫指導(dǎo)這些代碼什么時(shí)候干活的代碼。沒有很好的把握作者的思維,理解起來自然會(huì)難一些。需要自行調(diào)度,這是生成器協(xié)程相對(duì)于原生協(xié)程(async/await形式)的一個(gè)缺點(diǎn)。

知道了協(xié)程是怎么回事,那么它能用來干什么?協(xié)程自行讓出CPU來協(xié)作高效利用CPU,讓出的時(shí)機(jī)當(dāng)然應(yīng)該是程序阻塞時(shí)。什么地方會(huì)讓程序阻塞呢?用戶態(tài)的代碼鮮有阻塞,阻塞主要是系統(tǒng)調(diào)用。而系統(tǒng)調(diào)用的大頭是IO,所以協(xié)程的主要應(yīng)用場(chǎng)景在網(wǎng)絡(luò)編程。為了讓程序高性能、高并發(fā),程序應(yīng)該異步執(zhí)行不能阻塞。既然異步執(zhí)行,就需要通知和回調(diào),寫回調(diào)函數(shù)避免不了“回調(diào)地獄(callback hell)”的問題:代碼可讀性差,程序執(zhí)行流程散落在層層回調(diào)函數(shù)中等。解決回調(diào)地獄的方式主要有兩種:Promise和協(xié)程。協(xié)程能以同步的方式編寫代碼,在高性能網(wǎng)絡(luò)編程(IO密集型)中是推薦的。

再回過頭看PHP中的協(xié)程編程。PHP中基于生成器實(shí)現(xiàn)實(shí)現(xiàn)協(xié)程編程,優(yōu)先推薦使用RecoilPHPAmp等協(xié)程框架。這些框架已經(jīng)寫好了調(diào)度器,在其上開發(fā)直接寫生成器函數(shù),內(nèi)核會(huì)自動(dòng)調(diào)度執(zhí)行(想讓一個(gè)函數(shù)以協(xié)程方式調(diào)度執(zhí)行,在函數(shù)體內(nèi)加上yield即可)。如果不想用yield方式進(jìn)行協(xié)程編程,推薦swoole或其衍生框架,能做到類似golang的協(xié)程編程體驗(yàn),又能享受PHP的開發(fā)效率。

如果想用原生態(tài)的做PHP協(xié)程編程,類似鳥哥博客中的調(diào)度器必不可少。調(diào)度器調(diào)度協(xié)程執(zhí)行,協(xié)程中斷后控制權(quán)又回到調(diào)度器中。所以調(diào)度器應(yīng)該總是在主(事件)循環(huán)中,即CPU不在執(zhí)行協(xié)程,就應(yīng)當(dāng)在執(zhí)行調(diào)度器的代碼。無協(xié)程運(yùn)行時(shí),調(diào)度器應(yīng)當(dāng)自我阻塞避免消耗CPU(鳥哥博客中使用了內(nèi)置的select系統(tǒng)調(diào)用),等待事件到來再執(zhí)行相應(yīng)的協(xié)程。程序運(yùn)行期間,除了調(diào)度器阻塞,協(xié)程在運(yùn)行過程中不應(yīng)該調(diào)用阻塞API。

總結(jié)

在協(xié)程編程中,yield的主要作用是將控制權(quán)轉(zhuǎn)讓,無需糾結(jié)于其返回值(基本上yield返回的值會(huì)在下次執(zhí)行時(shí)直接send過來)。重點(diǎn)應(yīng)當(dāng)關(guān)注控制權(quán)轉(zhuǎn)讓的時(shí)機(jī),以及協(xié)程的運(yùn)作方式。

另外需要說明一點(diǎn),協(xié)程和異步?jīng)]有多大關(guān)系,還要看運(yùn)行環(huán)境支撐。常規(guī)的PHP運(yùn)行環(huán)境,即使用了promise/coroutine,也還是同步阻塞的。再牛逼的協(xié)程框架,sleep一下也不好使了。作為類比,即使JavaScript不使用promise/async這些技術(shù),也是異步非阻塞的。

通過生成器和Promise,能實(shí)現(xiàn)類似于await的協(xié)程編程,相關(guān)代碼在Github上很多,本文不再給出。

總結(jié)

本文先介紹了生成器的概念,重點(diǎn)是yield的用法及生成器的接口。協(xié)程部分則簡(jiǎn)要說了協(xié)程的原理,以及PHP協(xié)程編程中應(yīng)當(dāng)注意的事項(xiàng)。

感謝閱讀,歡迎指正!

參考

http://php.net/manual/zh/lang...

http://php.net/manual/zh/clas...

https://wiki.php.net/rfc/gene...

https://wiki.php.net/rfc/gene...

https://zhuanlan.zhihu.com/p/...

http://www.laruence.com/2015/...

https://medium.com/async-php/...

https://blog.kghost.info/2011...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/30868.html

相關(guān)文章

  • Swoole4.x協(xié)程變量訪問安全與協(xié)程連接池實(shí)現(xiàn)

    摘要:訪問安全問題為什么說有訪問安全問題呢傳統(tǒng)地,在的的環(huán)境中,很少有遇到所謂變量安全訪問問題。上下文管理器為了解決這個(gè)問題,我們引入?yún)f(xié)程上下文管理這樣的概念,由此來實(shí)現(xiàn)每個(gè)協(xié)程環(huán)境內(nèi)的數(shù)據(jù)隔離。 訪問安全問題 為什么說有訪問安全問題呢?傳統(tǒng)地,在php的的環(huán)境中,很少有Phper遇到所謂變量安全訪問問題。舉個(gè)例子,代碼大約如下: class db { protected stati...

    aisuhua 評(píng)論0 收藏0
  • 通讀Python官方文檔協(xié)程、Future與Task

    摘要:所以在第一遍閱讀官方文檔的時(shí)候,感覺完全是在夢(mèng)游。通過或者等待另一個(gè)協(xié)程的結(jié)果或者異常,異常會(huì)被傳播。接口返回的結(jié)果指示已結(jié)束,并賦值。取消與取消不同。調(diào)用將會(huì)向被包裝的協(xié)程拋出。任務(wù)相關(guān)函數(shù)安排協(xié)程的執(zhí)行。負(fù)責(zé)切換線程保存恢復(fù)。 Tasks and coroutines 翻譯的python官方文檔 這個(gè)問題的惡心之處在于,如果你要理解coroutine,你應(yīng)該理解future和tas...

    mgckid 評(píng)論0 收藏0
  • Python迭代器、生成器、裝飾器深入解讀

    摘要:前言首先,明確可迭代對(duì)象迭代器和生成器這三個(gè)概念。迭代器對(duì)象傳送門之迭代器實(shí)現(xiàn)原理首先明確它是一個(gè)帶狀態(tài)的對(duì)象。生成器是一種特殊的迭代器,它的返回值不是通過而是用。 前言 首先,明確可迭代對(duì)象、迭代器和生成器這三個(gè)概念。 可迭代對(duì)象(Iterable) 可迭代對(duì)象(Iterable Object),簡(jiǎn)單的來理解就是可以使用 for 來循環(huán)遍歷的對(duì)象。比如常見的 list、set和di...

    codercao 評(píng)論0 收藏0
  • Swoole協(xié)程之旅-前篇

    摘要:協(xié)程完全有用戶態(tài)程序控制,所以也被成為用戶態(tài)的線程。目前支持協(xié)程的語言有很多,例如等。協(xié)程之旅前篇結(jié)束,下一篇文章我們將深入分析原生協(xié)程部分的實(shí)現(xiàn)。 寫在最前 ??Swoole協(xié)程經(jīng)歷了幾個(gè)里程碑,我們需要在前進(jìn)的道路上不斷總結(jié)與回顧自己的發(fā)展歷程,正所謂溫故而知新,本系列文章將分為協(xié)程之旅前、中、后三篇。 前篇主要介紹協(xié)程的概念和Swoole幾個(gè)版本協(xié)程實(shí)現(xiàn)的主要方案技術(shù); 中篇主...

    terasum 評(píng)論0 收藏0
  • 低調(diào)奢華有內(nèi)涵 - 收藏集 - 掘金

    摘要:比較的是兩個(gè)對(duì)象的內(nèi)容是并發(fā)編程之協(xié)程異步后端掘金引言隨著的盛行,相信大家今年多多少少都聽到了異步編程這個(gè)概念。使用進(jìn)行并發(fā)編程篇二掘金我們今天繼續(xù)深入學(xué)習(xí)。 python 之機(jī)器學(xué)習(xí)庫(kù) scikit-learn - 后端 - 掘金一、 加載sklearn中的數(shù)據(jù)集datasets from sklearn import datasets iris = datasets.load_i...

    walterrwu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<