PHP協程內容的詳解

不言
發布: 2023-04-03 13:46:01
原創
1689 人瀏覽過

這篇文章跟大家分享了關於php協成的內容,有一定的參考價值,希望可以幫助到有需要的朋友。

 概念

咱們知道多進程和多執行緒是實現並發的有效方式。但多進程的上下文切換資源開銷太大;多線程開銷相比要小得多,也是現在主流的做法,但其的控制權在核,從而使用戶(程式設計師)失去了對程式碼的控制,而且線程的上下文切換也是有一定開銷的。 這時為了解決上述問題,"協程"(coroutine)的概念就產生了。你可以將協程理解為更輕量級的線程。這種線程叫做「用戶空間線程」。協程,有以下兩個特點:

  1. 協同。因為是由程式設計師自己寫的調度策略,其透過協作而不是搶佔來進行切換

  2. #在使用者態完成創建,切換和銷毀

PHP對協程的支援是在迭代生成器的基礎上, 增加了可以回傳資料給生成器的功能(呼叫者發送資料給被呼叫的生成器函數)。 這就把生成器到呼叫者的單向通訊轉變為兩者之間的雙向通訊。

迭代器

迭代器的概念這裡就不贅述了。下面看看我們自己實作的一個迭代器。

class MyIterator implements Iterator
{
    private $var = array();

    public function __construct($array)
    {
        if (is_array($array)) {
            $this->var = $array;
        }
    }

    public function rewind() {   // 第一次迭代时候会执行(或调用该方法的时候),后面的迭代将不会执行。
        echo "rewinding\n";
        reset($this->var);  
    }

    public function current() {
        $var = current($this->var);
        echo "current: $var\n";
        return $var;
    }

    public function key() {
        $var = key($this->var);
        echo "key: $var\n";
        return $var;
    }

    public function next() {    // 最后执行,就是执行完下面sleep(2)后再执行。(执行了next本次迭代才算结束)
        $var = next($this->var);
        echo "next: $var\n";
        return $var;
    }

    public function valid() {      // 当valid返回false的时候迭代结束
        $var = $this->current() !== false;
        echo "valid: {$var}\n";
        return $var;
    }
}

$values = array(1,2,3,4);
$it = new MyIterator($values);

foreach ($it as $a => $b) { // 进行迭代(每次迭代,会依次执行以下方法: rewind(特别之处见上面解释), valid, current, key, next)
    print "=====\n";
    sleep(2);
}
登入後複製

#輸出:

rewinding
current: 1  // 因为valid里面调用了current, 这里current出来一次
valid: 1
current: 1
key: 0
=====
next: 2
current: 2
valid: 1
current: 2
key: 1
=====
next: 3
current: 3
valid: 1
current: 3
key: 2
=====
next: 4
current: 4
valid: 1
current: 4
key: 3
=====
next: 
current: 
valid:    // valid返回false,迭代结束
登入後複製

#產生器

有了yeild的方法就是一個生成器(生成器實作了Iterator介面,即一個生成器有迭代器的特點)。生成器的實作如下:

function xrange($start, $end, $step = 1) {
    for ($i = $start; $i <= $end; $i += $step) {
        echo $i . "\n";
        yield;
    }
}

// foreach方式
foreach (xrange(1, 10) as $num) {
    
}

$gene = xrange(1, 10); // gene就是一个生成器对象
// current
$gene->current();  // 打印1
// next
$gene->next();
$gene->current()  // 打印2
登入後複製

輸出:

1
2
3
4
5
6
7
8
9
10
1
2
登入後複製

產生器各方法詳解可看文件: http://php.net/manual/zh/class.generator. php

注意:

生成器不能像函數一樣直接調用,呼叫方法如下:

1. foreach他

2. send ($value)  

3. current / next...

yield

yield的語法很靈活,我們用下面的例子,讓大家都能明白yield文法的使用。

用例1: 讓出cpu執行權

function task1 () {
for ($i = 1; $i <= 10; ++$i) {
        echo "This is task 1 iteration $i.\n";
        yield;// 遇到yield就会主动让出CPU的执行权;
    }
}
$a = task1(); 
$a->current(); // 执行第一次迭代
$a->send(1);  // 唤醒当时让出CPU执行权的yield
登入後複製

輸出:

This is task 1 iteration 1.
This is task 1 iteration 2.
登入後複製

用例2: yield的回傳

// yield返回
function task2 () {
    for ($i = 1; $i <= 10; ++$i) {
            echo "This is task 2 iteration $i.\n";
            yield "lm$i";  // 遇到yield就会主动让出CPU的执行权,for暂停执行, 然后返回"lm"。放在yield后面的值就是返回值
        }
}

$a = task2(); 
$res = $a->current();  // 第一次迭代, 遇到yield返回
var_dump($res);  
$res = $a->send(1);  // 唤醒yield, for继续执行,遇到yield返回。
var_dump($res);
登入後複製

#輸出:

This is task 2 iteration 1.
string(3) "lm1"
This is task 2 iteration 2.
string(3) "lm2"
登入後複製

用例3: yield接收值

function task3 () {
    for ($i = 1; $i <= 10; ++$i) {
            echo "This is task 3 iteration $i.\n";
            $getValue = yield;// 遇到yield就会主动让出CPU的执行权;send后,将send值赋值给getValue
            echo $getValue . " ";
        }
}
$a = task3(); 
$a->current();
$a->send("aa");  // 唤醒yield,并将"aa"值赋值给$getValue变量
登入後複製

輸出:

This is task 3 iteration 1.
aa This is task 3 iteration 2.
登入後複製

用例4: yeild接收和回傳寫在一起

function task4 () {
    for ($i = 1; $i <= 10; ++$i) {
        echo "This is task 4 iteration $i.\n";
        $ret = yield "lm$i";  // yield, 然后返回lm$i; 当send时,将send过来的值赋值给$ret;
        echo $ret;
    }
}
$a = task4(); 
var_dump($a->current());     // 返回lm1
var_dump($a->send("hhh "));  // 先唤醒yield, 将"hhh "赋值给$ret,再返回lm2
var_dump($a->send("www "));  // 先唤醒yield, 将"www "赋值给$ret,再返回lm3
登入後複製

#輸出:

This is task 4 iteration 1.
string(3) "lm1"hhh 
This is task 4 iteration 2.
 string(3) "lm2"www 
 This is task 4 iteration 3.
 string(3) "lm3"
登入後複製

相關推薦:

PHP中抽象類別和介面的使用方法(程式碼)

php分頁和正規驗證的程式碼實作

以上是PHP協程內容的詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!