ホームページ  >  記事  >  バックエンド開発  >  PHPのコルーチンとブロッキングについての深い理解

PHPのコルーチンとブロッキングについての深い理解

黄舟
黄舟オリジナル
2017-08-11 11:37:001620ブラウズ

この記事では、主に PHP のコルーチンとブロックについての理解と考え方を、サンプル コードを通じて詳しく紹介しています。PHP を学習または使用する人にとって、一定の参考となる価値があります。必要な方は以下を参照してください。一緒に学びましょう。

前書き

この記事では主に、PHP のコルーチンとブロックについての理解と考え方を紹介し、参考と学習のために共有します。以下では多くのことを説明しません。詳細な紹介を見てみましょう。

プロセス、スレッド、コルーチン

プロセス、スレッド、コルーチンについては、非常に詳細で充実したブログや学習リソースがありますので、ここでは詳しく説明しません。

  • プロセスには独自の独立したヒープとスタックがあり、ヒープもスタックも共有しません。プロセスはオペレーティング システムによってスケジュールされます。

  • スレッドには独自の独立したスタックと共有ヒープがあります。ヒープは共有されますが、スレッドもオペレーティング システムによってスケジュールされます (標準スレッドは共有されます)。

  • コルーチンはスレッドと同様にヒープを共有しますが、スタックは共有しません。コルーチンは、コルーチンのコード内でプログラマによって明示的にスケジュールされます。

PHP でのコルーチン yield の基本的な実装

yield の基本的な実装はジェネレーター クラスであり、イテレーター クラスはイテレーター インターフェイスの実装です。彼が実装したコルーチンのマルチタスク スケジューリングについて、例を挙げて、ブロックに関する私の考えをいくつかお話します。


単純なスケジュールされた実行タスクのカスタマイズの例:

(この例は、上記の Brother Niao によって実装されたコルーチン スケジューリング コードに依存する必要があります)

Generator implements Iterator {
 public mixed current ( void ) // 返回当前产生的值
 public mixed key ( void ) // 返回当前产生的键
 public void next ( void ) // 生成器继续执行
 public void rewind ( void ) // 重置迭代器,如果迭代已经开始了,这里会抛出一个异常。
   // renwind的执行将会导致第一个yield被执行, 并且忽略了他的返回值.
 public mixed send ( mixed $value ) // 向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。如果当这个方法被调用时,生成器 
   // 不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。
 public void throw ( Exception $exception ) // 向生成器中抛入一个异常
 public bool valid ( void ) // 检查迭代器是否被关闭
 public void __wakeup ( void ) // 序列化回调,抛出一个异常以表示生成器不能被序列化。
}

上記の実装は次のとおりです:


2 つを生成しますタスクを並列実行し、実行中に各タスクのブロックを数秒間シミュレートします。

    コルーチンが切り替わるときにスムーズに切り替えることができ、タスクのブロックは相互に影響しません。
  • なぜ上記のことをしなければならないのですか?コルーチンの実装は非常に強力で興味深いものであり、複数のタスクを並行して実行できるにもかかわらず、タスクの 1 つでシステム関数 sleep() を呼び出すと、ブロックしているタスクがコルーチンの切り替えを防止します。実際、これはコルーチンの実装原則にも当てはまります。
  • 次に、コルーチンのブロックもシミュレートしたいと思いますが、それが実現可能かどうかを確認するためにブロックを発生させません。 PHP 自体は、コルーチン呼び出しをサポートするためのジェネレーターのみを提供します。拡張機能に依存しない場合は、Java ほど強力ではなく、サブスレッドを開くことで実装できます。

    Javaのサブスレッドは独立して実行され、お互いにブロックしない印象があるので、PHPではマルチスレッドに近い仕組みが実装できるので、呼び出し処理中にノンブロッキングを実現できるのではないかと考えています。
このように実装して考えた結果、最初に誤解に陥ったのは、PHP ネイティブ関数 sleep() のブロックによる誤解でした。本当にノンブロッキングを実現するには、言い換えれば、非同期に実装するには、基礎となる言語に依存する必要があります。

後で、私は真実を理解しました。特定のメソッドまたは関数は実行中にブロックされるため、現在のメソッドをカスタムメソッドに置き換えて、(コルーチンのスケジューリング全体と比較して)非ブロックにします。それだけでは十分ではありません。 ?たとえば、上記のスケジュールされた実行を自分で実装しました。
一方、コルーチン スケジューリング自体の目的は、タスクの実行プロセスをできるだけ小さく切り分けて、実行を迅速に切り替えて並列処理を実現することです。この観点から、コルーチンはプログラミングのアイデアともみなされる必要があります。

sleep() 的时候,阻塞任务会阻止协程切换,其实从协程的实现原理上来书也是这么回事。

那么,我也就想模拟协程阻塞,但是不产生阻塞看是否可行。PHP本身只提供了生成器为协程调用提供了支撑,如果不依赖扩展,没有提供多线程的程序实现方式,没有java那么强大,可以开子线程进行实现。

我印象中java的子线程是独立执行且不会相互阻塞的,所以我在想,PHP既然可以实现类似于多线程这样的机制,那么能不能实现调用过程中非阻塞呢?

经过这样一个实现和思考,一开始是陷入了一个误区的,是由于PHP原生函数 sleep() 以下は、実行のためにプログラムをできるだけ小さな部分に分割する例です:

class timer {
 private $start = 0; // 定时开始时间
 private $timer; // 间隔的时间差,单位秒
 private $value = 0; // 产生的结果值
 private $callback; // 异步回调
 private $isEnd = false; // 当前定时器任务是否结束
 public function __construct($timer,callable $callback)
 {
 $this->start = time();
 $this->timer = $timer;
 $this->callback = $callback;
 }
 public function run() {
 if($this->valid()) {
 $callback = $this->callback;
 $callback($this->value ++,$this);
 $this->start = time();
 }
 }
 /**
 * 定时执行检查
 */
 public function valid() {
 $end = time();
 if($end - $this->start >= $this->timer) {
 return true;
 } else {
 return false;
 }
 }
 public function setEnd($isEnd) {
 $this->isEnd = $isEnd;
 }
 public function getEnd() {
 return $this->isEnd;
 }
}

/**
 * 模拟阻塞的协程1
 *
 */
function taskObject1() {
 $timer = new timer(1,function($value,timer $timer) {
 if($value >= 5) {
 $timer->setEnd(true);
 }
 echo &#39;<br>&#39;.&#39;A &#39;.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
/**
 * 模拟阻塞的协程2
 *
 */
function taskObject2() {
 $timer = new timer(2,function($value,timer $timer) {
 if($value >= 3) {
 $timer->setEnd(true);
 }
 echo &#39;<br>&#39;.&#39;B &#39;.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
$scheduler = new Scheduler;
$scheduler->newTask(taskObject1());
$scheduler->newTask(taskObject2());
$scheduler->run();
この例では、range を使用して大きな整数配列を生成する元の方法から、スライス実行に切り替えます。つまり、トラバース時に指定された値が取得されるため、コードの観点から見ると、メモリ消費量は以前に比べて非常に少なくなります。

概要

以上がPHPのコルーチンとブロッキングについての深い理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。