PHP 協程


協程,又稱微線程,纖程。英文名Coroutine。
協程的概念很早就提出來了,但直到最近幾年才在某些語言(如Lua)中得到廣泛應用。

子程序,或者稱為函數,在所有語言中都是層級調用,比如A調用B,B在執行過程中又調用了C,C執行完畢返回,B執行完畢返回,最后是A執行完畢。

所以子程序調用是通過棧實現的,一個線程就是執行一個子程序。子程序調用總是一個入口,一次返回,調用順序是明確的。而協程的調用和子程序不同。

協程看上去也是子程序,但執行過程中,在子程序內部可中斷,然后轉而執行別的子程序,在適當的時候再返回來接着執行。

注意,在一個子程序中中斷,去執行其他子程序,不是函數調用,有點類似CPU的中斷。比如子程序A、B:

def A(): 
    print '1' 
    print '2' 
    print '3'

def B(): 
    print 'x' 
    print 'y' 
    print 'z'

 

假設由協程執行,在執行A的過程中,可以隨時中斷去執行B,B也可能中斷再去執行A,結果可能是:12xy3z

看起來A、B的執行有點像多線程,但協程的特點在於是一個線程執行,那和多線程比,協程有何優勢?

最大的優勢就是協程極高的執行效率。因為子程序切換不是線程切換,而是由程序自身控制,因此沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優勢就越明顯。

第二大優勢就是不需要多線程的鎖機制,因為只有一個線程,也不存在同時寫變量沖突,在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多線程高很多。

來看例子:

傳統的生產者-消費者模型是一個線程寫消息,一個線程取消息,通過鎖機制控制隊列和等待,但一不小心就可能死鎖。如果改用協程,生產者生產消息后,直接通過yield跳轉到消費者開始執行,待消費者執行完畢后,切換回生產者繼續生產,效率極高:

import time

def consumer(): r = '' 
  while True: 
    n = yield r 
    if not n: return 
    print('[消費者] Consuming %s...' % n) 
    time.sleep(1) 
    r = '200 OK'

def produce(c): 
  c.next() 
  n = 0 
  while n < 5: 
    n = n + 1 
    print('[生產者] Producing %s...' % n) 
    r = c.send(n) 
    print('[生產者] Consumer return: %s' % r) 
  c.close()

if __name__=='__main__': 
c = consumer() 
produce(c)

執行結果:

[生產者] Producing 1...
[消費者] Consuming 1...
[ 生產者] Consumer return: 200 OK

[ 生產者] Producing 2...
[消費者] Consuming 2...
[生產者] Consumer return: 200 OK

注意到consumer函數是一個generator(生成器),把一個consumer傳入produce后:

首先調用c.next()啟動生成器;

然后,一旦生產了東西,通過c.send(n) 切換到 consumer執行;
consumer 通過 yield 拿到消息,處理,又通過yield把結果傳回;
produce 拿到 consumer 處理的結果,繼續生產下一條消息;
produce決 定不生產了,通過c.close()關閉consumer,整個過程結束。

整個流程無鎖,由一個線程執行,produce和consumer協作完成任務,所以稱為“協程”,而非線程的搶占式多任務。一句話總結協程的特點:“子程序就是協程的一種特例。”

如果上面的例子,你看懂了,下面來講講PHP的協程。

//生成器函數
function xrange($start, $end, $step = 1) {

  for ($i = $start; $i <= $end; $i += $step) {

      //返回一個Generator 對象,並暫停,遍歷 Generator對象時,線程會回到這里,繼續執行。
      yield $i;
  }
}

//調用成器函數
$generator = xrange(1, 10);

//打印對象
print_r($generator);//Generator Object()

//遍歷$generator 對象
foreach ($generator as $value) {
    echo "$value\n";
}

 

遍歷目錄

 

function loopdir($dir){

    $list = scandir($dir);
    if($list === false) $list = array();

    foreach($list as $file){

        if($file == '.' || $file == '..') continue;

        $file = $dir . '/' . $file;
        if(is_dir($file)){

            foreach(loopdir($file) as $value){
                yield $value;
            }
        }else{
            yield $file;
        }
    }
}



$generator = loopdir('/home/zbseoag/Downloads/elasticsearch-head');

foreach ($generator as $value){
   print_r($value);
   echo '<br/>';
}

 

 

以上就是 PHP 協程最基本的應用了。通過協程,可以處理很多消耗內存的任務。比如:讀取和分割大文件。

可以查看鳥哥文章:在PHP中使用協程實現多任務調度 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM