Heim  >  Artikel  >  Backend-Entwicklung  >  Vertieftes Verständnis von Coroutinen und Blockierung in PHP

Vertieftes Verständnis von Coroutinen und Blockierung in PHP

黄舟
黄舟Original
2017-08-11 11:37:001659Durchsuche

Dieser Artikel führt Sie hauptsächlich in das Verständnis und Nachdenken über Coroutinen und Blockierung in PHP ein. Der Artikel stellt es im Detail anhand von Beispielcode vor. Es hat einen gewissen Referenz-Lernwert für alle, die es brauchen. Lasst uns lernen mit dem Herausgeber unten.

Vorwort

Dieser Artikel führt Sie hauptsächlich in das Verständnis und die Überlegungen zu Coroutinen und Blockierung in PHP ein und stellt es Ihnen als Referenz und zum Studium zur Verfügung . Im Folgenden gibt es nicht viel zu sagen. Werfen wir einen Blick auf die ausführliche Einführung:

Prozess, Thread, Coroutine

Über den Prozess , Threads und Coroutinen, es gibt sehr detaillierte und umfangreiche Blogs oder Lernressourcen, auf die ich hier nicht näher eingehen werde.

  • Der Prozess verfügt über einen eigenen unabhängigen Heap und Stack. Er teilt weder den Heap noch den Stack. Der Prozess wird vom Betriebssystem geplant.

  • Threads haben ihren eigenen unabhängigen Stapel und gemeinsam genutzten Heap, aber der Stapel wird auch vom Betriebssystem geplant (Standard-Threads sind es).

  • Coroutinen teilen sich den Heap wie Threads, aber keine Stacks. Coroutinen werden von Programmierern explizit im Code der Coroutine geplant.

Grundlegende Implementierung von Coroutine in PHP yield

Die grundlegende Implementierung von yield ist Generieren Sie eine Iterator-Klasse, und die Iterator-Klasse ist die Implementierung der Iterator-Schnittstelle:


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 ) // 序列化回调,抛出一个异常以表示生成器不能被序列化。
}

Ich werde es basierend auf der Coroutine-Multitask-Planung er tun Ich werde Ihnen ein Beispiel geben und über einige meiner Gedanken zum Blockieren sprechen.

Beispiel für die Anpassung einfacher geplanter Ausführungsaufgaben:

(Dieses Beispiel muss auf dem von Bruder Niao oben implementierten Coroutine-Planungscode basieren )


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();

Die obige Implementierung ist:

  • Generieren Sie zwei Aufgaben, führen Sie sie parallel aus und geben Sie jeder Aufgabe eine Simulieren Sie ein paar Sekunden Blockierung während der Ausführung.

  • ermöglicht einen reibungslosen Wechsel, wenn Coroutinen gewechselt werden, und die Aufgabenblockierung wirkt sich nicht gegenseitig aus

Denken:

Warum sollte ich das oben Gesagte tun? Denn ich habe festgestellt, dass die Coroutine-Implementierung zwar sehr leistungsfähig und interessant ist und Multitasking parallel ermöglichen kann, die Blockierungsaufgabe jedoch den Coroutine-Wechsel verhindert, wenn ich die Systemfunktion sleep() in einer der Aufgaben aufrufe Das Implementierungsprinzip von Coroutine gilt auch für Bücher.

Dann möchte ich auch das Coroutine-Blockieren simulieren, aber kein Blockieren verursachen, um zu sehen, ob es machbar ist. PHP selbst bietet nur Generatoren zur Unterstützung von Coroutine-Aufrufen. Wenn es nicht auf Erweiterungen angewiesen ist, bietet es keine Multithread-Programmimplementierungsmethode. Es ist nicht so leistungsfähig wie Java und kann durch Öffnen von Sub-Threads implementiert werden.

Ich habe den Eindruck, dass die Sub-Threads von Java unabhängig voneinander ausgeführt werden und sich nicht gegenseitig blockieren. Daher denke ich, dass PHP, da es einen Mechanismus ähnlich dem Multithreading implementieren kann, eine Nichtblockierung während des Aufrufs erreichen kann Verfahren? ?

Nach einer solchen Implementierung und Überlegung geriet ich am Anfang in ein Missverständnis. Es war ein Missverständnis, das durch die Blockierung der nativen PHP-Funktion verursacht wurde sleep() , das heißt, wenn ich wirklich nicht erreichen möchte. blockierende oder asynchrone Implementierung, muss von der zugrunde liegenden Sprache abhängen.

Später habe ich eine Wahrheit herausgefunden. Da eine bestimmte Methode oder Funktion während der Ausführung blockiert, ersetzen Sie die aktuelle Methode durch eine benutzerdefinierte und machen Sie sie nicht blockierend (im Vergleich zum gesamten Protokoll). der Terminplanung, wäre das nicht genug? Ich habe zum Beispiel die oben genannte geplante Ausführung selbst umgesetzt.

Andererseits besteht der Zweck der Coroutine-Planung selbst darin, den Aufgabenausführungsprozess in einen möglichst kleinen Teil zu zerlegen, um die Ausführung schnell zu ändern und Parallelität zu erreichen. Aus dieser Perspektive sollte Coroutine auch als Programmieridee betrachtet werden.

Das Folgende ist ein Beispiel für das Zerlegen eines Programms in möglichst kleine Stücke zur Ausführung:


// 一个简单的例子
<?php
function xrange($start, $end, $step = 1) {
 for ($i = $start; $i <= $end; $i += $step) {
 yield $i;
 }
}
 
foreach (xrange(1, 1000000) as $num) {
 echo $num, "\n";
}

In diesem Beispiel wird der ursprüngliche Bereich zum Generieren eines großen Integer-Arrays in eine Shard-Ausführung umgewandelt, was bedeutet, dass der angegebene Wert beim Durchlaufen erhalten wird. Aus Code-Sicht ist der Speicherverbrauch im Vergleich zu zuvor sehr gering.

Zusammenfassung

Das obige ist der detaillierte Inhalt vonVertieftes Verständnis von Coroutinen und Blockierung in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn