


Besprechen Sie ausführlich die Implementierung von Coroutinen unter PHP7
Vorwort
Ich glaube, jeder hat vom Konzept der „Coroutine“ gehört.
Aber einige Schüler verstehen dieses Konzept nicht und wissen nicht, wie sie es umsetzen, wie sie es verwenden und wo sie es verwenden sollen. Manche Leute denken sogar, dass Ertrag Coroutine ist!
Ich glaube immer, wenn man einen Wissenspunkt nicht genau ausdrücken kann, kann ich denken, dass man ihn einfach nicht versteht.
Wenn Sie schon einmal etwas über die Verwendung von PHP zur Implementierung von Coroutinen gelernt haben, müssen Sie den Artikel von Bruder Niao gelesen haben: Verwenden von Coroutinen zur Implementierung von Multitask-Scheduling in PHP | Wind and Snow Corner
Artikel von Bruder Niao wurde von einem ausländischen Autor übersetzt. Die Übersetzung ist prägnant und klar, und es werden auch konkrete Beispiele gegeben.
Der Zweck des Schreibens dieses Artikels besteht darin, den Artikel von Bruder Niao vollständiger zu ergänzen. Schließlich ist die Grundlage einiger Schüler nicht gut genug und sie sind verwirrt.
Was ist eine Coroutine?
Lassen Sie uns zunächst herausfinden, was eine Coroutine ist.
Vielleicht haben Sie schon von den Konzepten „Prozess“ und „Thread“ gehört.
Ein Prozess ist eine laufende Instanz einer binären ausführbaren Datei im Computerspeicher, genau wie Ihre .exe-Datei eine Klasse ist und der Prozess die neue Instanz ist.
Der Prozess ist die Grundeinheit der Ressourcenzuweisung und -planung im Computersystem (machen Sie sich keine Sorgen über Thread-Prozesse in der Planungseinheit. Jede CPU kann nur einen Prozess gleichzeitig verarbeiten).
Die sogenannte Parallelität sieht nur parallel aus. Die CPU wechselt tatsächlich sehr schnell zwischen verschiedenen Prozessen.
Der Prozesswechsel erfordert einen Systemaufruf. Die CPU muss verschiedene Informationen über den aktuellen Prozess speichern, außerdem wird der CPUCache zerstört.
Wenn Sie also nicht den Prozess wechseln können, tun Sie es nicht, es sei denn, Sie müssen es tun.
Wie kann man also erreichen, dass „Prozesswechsel nur dann durchgeführt werden, wenn es notwendig ist“?
Zuallererst sind die Bedingungen für das Umschalten eines Prozesses: Die Prozessausführung ist abgeschlossen, die dem Prozess zugewiesene CPU-Zeitscheibe endet, im System tritt ein Interrupt auf, der verarbeitet werden muss, oder die Prozess wartet auf notwendige Ressourcen (Prozessblockierung) usw. Wenn Sie darüber nachdenken, gibt es in den vorherigen Situationen nichts zu sagen, aber wenn Sie blockieren und warten, ist das eine Verschwendung?
Tatsächlich gibt es, wenn es blockiert ist, andere ausführbare Orte, an denen unser Programm ausgeführt werden kann, sodass wir nicht dumm warten müssen!
Es gibt also Threads.
Ein einfaches Verständnis eines Threads ist ein „Mikroprozess“, der speziell eine Funktion ausführt (logischer Fluss).
So können wir Threads verwenden, um Funktionen zu verkörpern, die während des Programmschreibprozesses gleichzeitig ausgeführt werden können.
Es gibt zwei Arten von Threads, einer wird vom Kernel verwaltet und geplant.
Wir sagen, dass die Kosten sehr hoch sind, solange der Kernel an der Verwaltung und Planung beteiligt ist. Diese Art von Thread löst tatsächlich das Problem, dass wir, wenn ein Thread, der in einem Prozess ausgeführt wird, auf eine Blockade stößt, die Ausführung eines anderen ausführbaren Threads planen können, dieser sich jedoch immer noch im selben Prozess befindet, sodass kein Prozesswechsel stattfindet.
Es gibt eine andere Art von Thread, dessen Planung von Programmierern verwaltet wird, die selbst Programme schreiben, und für den Kernel unsichtbar ist. Diese Art von Thread wird als „User-Space-Thread“ bezeichnet.
Coroutine kann als eine Art User-Space-Thread verstanden werden.
Coroutine weist mehrere Merkmale auf:
- Zusammenarbeit, da es sich um eine vom Programmierer selbst geschriebene Planungsstrategie handelt, wechselt sie durch Zusammenarbeit statt Vorkaufsrecht
- Vollständige Erstellung, Umschalten und Zerstören im Benutzermodus
- ⚠️ Aus Programmiersicht ist die Idee von Coroutine im Wesentlichen die aktive Ausbeute und Wiederaufnahme des Kontrollflussmechanismus
- Generator wird häufig zum Implementieren von Coroutinen verwendet
Vor diesem Hintergrund sollten Sie das Grundkonzept von Coroutinen verstehen, oder?
PHP-Implementierung von Coroutine
Schritt für Schritt, beginnend mit der Erklärung des Konzepts!
Iterierbares Objekt
PHP5 bietet eine Möglichkeit, ein Objekt so zu definieren, dass es durch eine Liste von Zellen durchlaufen werden kann, beispielsweise mit der foreach
-Anweisung.
Wenn Sie ein iterierbares Objekt implementieren möchten, müssen Sie die Iterator
Schnittstelle implementieren:
<?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() { $var = next($this->var); echo "next: $var\n"; return $var; } public function valid() { $var = $this->current() !== false; echo "valid: {$var}\n"; return $var; } } $values = array(1,2,3); $it = new MyIterator($values); foreach ($it as $a => $b) { print "$a: $b\n"; }
Generator
Das kann man sagen Damit ein Objekt von foreach
durchlaufen werden kann, müssen Sie eine Reihe von Methoden implementieren. Das Schlüsselwort yield
soll diesen Prozess vereinfachen. Der
-Generator bietet eine einfachere Möglichkeit, eine einfache Objektiteration zu implementieren. Im Vergleich zur Definition einer Klasse zur Implementierung der Iterator
-Schnittstelle werden der Leistungsaufwand und die Komplexität erheblich reduziert.
<?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"; }
Wenn yield
in einer Funktion verwendet wird, ist es sinnlos, es direkt als Funktion aufzurufen !
Also, yield
ist yield
, wenn das nächste Mal, wer yield
sagt, eine Coroutine ist, werde ich dich auf jeden Fall xxxx behandeln.
PHP-Coroutine
Wie bereits bei der Einführung von Coroutinen erwähnt, erfordern Coroutinen, dass Programmierer den Planungsmechanismus selbst schreiben. Schauen wir uns an, wie dieser Mechanismus geschrieben wird.
0) Generatoren richtig verwenden
Da Generatoren nicht wie Funktionen direkt aufgerufen werden können, wie können sie dann aufgerufen werden?
方法如下:
- foreach他
- send($value)
- current / next...
1)Task实现
Task就是一个任务的抽象,刚刚我们说了协程就是用户空间协程,线程可以理解就是跑一个函数。
所以Task的构造函数中就是接收一个闭包函数,我们命名为coroutine
。
/** * Task任务类 */ class Task { protected $taskId; protected $coroutine; protected $beforeFirstYield = true; protected $sendValue; /** * Task constructor. * @param $taskId * @param Generator $coroutine */ public function __construct($taskId, Generator $coroutine) { $this->taskId = $taskId; $this->coroutine = $coroutine; } /** * 获取当前的Task的ID * * @return mixed */ public function getTaskId() { return $this->taskId; } /** * 判断Task执行完毕了没有 * * @return bool */ public function isFinished() { return !$this->coroutine->valid(); } /** * 设置下次要传给协程的值,比如 $id = (yield $xxxx),这个值就给了$id了 * * @param $value */ public function setSendValue($value) { $this->sendValue = $value; } /** * 运行任务 * * @return mixed */ public function run() { // 这里要注意,生成器的开始会reset,所以第一个值要用current获取 if ($this->beforeFirstYield) { $this->beforeFirstYield = false; return $this->coroutine->current(); } else { // 我们说过了,用send去调用一个生成器 $retval = $this->coroutine->send($this->sendValue); $this->sendValue = null; return $retval; } } }
2)Scheduler实现
接下来就是Scheduler
这个重点核心部分,他扮演着调度员的角色。
/** * Class Scheduler */ Class Scheduler { /** * @var SplQueue */ protected $taskQueue; /** * @var int */ protected $tid = 0; /** * Scheduler constructor. */ public function __construct() { /* 原理就是维护了一个队列, * 前面说过,从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制 * */ $this->taskQueue = new SplQueue(); } /** * 增加一个任务 * * @param Generator $task * @return int */ public function addTask(Generator $task) { $tid = $this->tid; $task = new Task($tid, $task); $this->taskQueue->enqueue($task); $this->tid++; return $tid; } /** * 把任务进入队列 * * @param Task $task */ public function schedule(Task $task) { $this->taskQueue->enqueue($task); } /** * 运行调度器 */ public function run() { while (!$this->taskQueue->isEmpty()) { // 任务出队 $task = $this->taskQueue->dequeue(); $res = $task->run(); // 运行任务直到 yield if (!$task->isFinished()) { $this->schedule($task); // 任务如果还没完全执行完毕,入队等下次执行 } } } }
这样我们基本就实现了一个协程调度器。
你可以使用下面的代码来测试:
<?php function task1() { for ($i = 1; $i <= 10; ++$i) { echo "This is task 1 iteration $i.\n"; yield; // 主动让出CPU的执行权 } } function task2() { for ($i = 1; $i <= 5; ++$i) { echo "This is task 2 iteration $i.\n"; yield; // 主动让出CPU的执行权 } } $scheduler = new Scheduler; // 实例化一个调度器 $scheduler->addTask(task1()); // 添加不同的闭包函数作为任务 $scheduler->addTask(task2()); $scheduler->run();
关键说下在哪里能用得到PHP协程。
function task1() { /* 这里有一个远程任务,需要耗时10s,可能是一个远程机器抓取分析远程网址的任务,我们只要提交最后去远程机器拿结果就行了 */ remote_task_commit(); // 这时候请求发出后,我们不要在这里等,主动让出CPU的执行权给task2运行,他不依赖这个结果 yield; yield (remote_task_receive()); ... } function task2() { for ($i = 1; $i <= 5; ++$i) { echo "This is task 2 iteration $i.\n"; yield; // 主动让出CPU的执行权 } }
这样就提高了程序的执行效率。
关于『系统调用』的实现,鸟哥已经讲得很明白,我这里不再说明。
3)协程堆栈
鸟哥文中还有一个协程堆栈的例子。
我们上面说过了,如果在函数中使用了yield
,就不能当做函数使用。
所以你在一个协程函数中嵌套另外一个协程函数:
<?php function echoTimes($msg, $max) { for ($i = 1; $i <= $max; ++$i) { echo "$msg iteration $i\n"; yield; } } function task() { echoTimes('foo', 10); // print foo ten times echo "---\n"; echoTimes('bar', 5); // print bar five times yield; // force it to be a coroutine } $scheduler = new Scheduler; $scheduler->addTask(task()); $scheduler->run();
这里的echoTimes是执行不了的!所以就需要协程堆栈。
不过没关系,我们改一改我们刚刚的代码。
把Task中的初始化方法改下,因为我们在运行一个Task的时候,我们要分析出他包含了哪些子协程,然后将子协程用一个堆栈保存。(C语言学的好的同学自然能理解这里,不理解的同学我建议去了解下进程的内存模型是怎么处理函数调用)
/** * Task constructor. * @param $taskId * @param Generator $coroutine */ public function __construct($taskId, Generator $coroutine) { $this->taskId = $taskId; // $this->coroutine = $coroutine; // 换成这个,实际Task->run的就是stackedCoroutine这个函数,不是$coroutine保存的闭包函数了 $this->coroutine = stackedCoroutine($coroutine); }
当Task->run()的时候,一个循环来分析:
/** * @param Generator $gen */ function stackedCoroutine(Generator $gen) { $stack = new SplStack; // 不断遍历这个传进来的生成器 for (; ;) { // $gen可以理解为指向当前运行的协程闭包函数(生成器) $value = $gen->current(); // 获取中断点,也就是yield出来的值 if ($value instanceof Generator) { // 如果是也是一个生成器,这就是子协程了,把当前运行的协程入栈保存 $stack->push($gen); $gen = $value; // 把子协程函数给gen,继续执行,注意接下来就是执行子协程的流程了 continue; } // 我们对子协程返回的结果做了封装,下面讲 $isReturnValue = $value instanceof CoroutineReturnValue; // 子协程返回`$value`需要主协程帮忙处理 if (!$gen->valid() || $isReturnValue) { if ($stack->isEmpty()) { return; } // 如果是gen已经执行完毕,或者遇到子协程需要返回值给主协程去处理 $gen = $stack->pop(); //出栈,得到之前入栈保存的主协程 $gen->send($isReturnValue ? $value->getValue() : NULL); // 调用主协程处理子协程的输出值 continue; } $gen->send(yield $gen->key() => $value); // 继续执行子协程 } }
然后我们增加echoTime的结束标示:
class CoroutineReturnValue { protected $value; public function __construct($value) { $this->value = $value; } // 获取能把子协程的输出值给主协程,作为主协程的send参数 public function getValue() { return $this->value; } } function retval($value) { return new CoroutineReturnValue($value); }
然后修改echoTimes
:
function echoTimes($msg, $max) { for ($i = 1; $i <= $max; ++$i) { echo "$msg iteration $i\n"; yield; } yield retval(""); // 增加这个作为结束标示 }
Task
变为:
function task1() { yield echoTimes('bar', 5); }
这样就实现了一个协程堆栈,现在你可以举一反三了。
4)PHP7中yield from关键字
PHP7中增加了yield from
,所以我们不需要自己实现携程堆栈,真是太好了。
把Task的构造函数改回去:
public function __construct($taskId, Generator $coroutine) { $this->taskId = $taskId; $this->coroutine = $coroutine; // $this->coroutine = stackedCoroutine($coroutine); //不需要自己实现了,改回之前的 }
echoTimes
函数:
function echoTimes($msg, $max) { for ($i = 1; $i <= $max; ++$i) { echo "$msg iteration $i\n"; yield; } }
task1
生成器:
function task1() { yield from echoTimes('bar', 5); }
这样,轻松调用子协程。
总结
这下应该明白怎么实现PHP协程了吧?
End...
推荐教程:《php教程》
Das obige ist der detaillierte Inhalt vonBesprechen Sie ausführlich die Implementierung von Coroutinen unter PHP7. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen





PHP 8.4 bringt mehrere neue Funktionen, Sicherheitsverbesserungen und Leistungsverbesserungen mit einer beträchtlichen Menge an veralteten und entfernten Funktionen. In dieser Anleitung wird erklärt, wie Sie PHP 8.4 installieren oder auf PHP 8.4 auf Ubuntu, Debian oder deren Derivaten aktualisieren. Obwohl es möglich ist, PHP aus dem Quellcode zu kompilieren, ist die Installation aus einem APT-Repository wie unten erläutert oft schneller und sicherer, da diese Repositorys in Zukunft die neuesten Fehlerbehebungen und Sicherheitsupdates bereitstellen.

Wenn Sie ein erfahrener PHP-Entwickler sind, haben Sie möglicherweise das Gefühl, dass Sie dort waren und dies bereits getan haben. Sie haben eine beträchtliche Anzahl von Anwendungen entwickelt, Millionen von Codezeilen debuggt und eine Reihe von Skripten optimiert, um op zu erreichen

Visual Studio Code, auch bekannt als VS Code, ist ein kostenloser Quellcode-Editor – oder eine integrierte Entwicklungsumgebung (IDE) –, die für alle gängigen Betriebssysteme verfügbar ist. Mit einer großen Sammlung von Erweiterungen für viele Programmiersprachen kann VS Code c

JWT ist ein offener Standard, der auf JSON basiert und zur sicheren Übertragung von Informationen zwischen Parteien verwendet wird, hauptsächlich für die Identitätsauthentifizierung und den Informationsaustausch. 1. JWT besteht aus drei Teilen: Header, Nutzlast und Signatur. 2. Das Arbeitsprinzip von JWT enthält drei Schritte: Generierung von JWT, Überprüfung von JWT und Parsingnayload. 3. Bei Verwendung von JWT zur Authentifizierung in PHP kann JWT generiert und überprüft werden, und die Funktionen und Berechtigungsinformationen der Benutzer können in die erweiterte Verwendung aufgenommen werden. 4. Häufige Fehler sind Signaturüberprüfungsfehler, Token -Ablauf und übergroße Nutzlast. Zu Debugging -Fähigkeiten gehört die Verwendung von Debugging -Tools und Protokollierung. 5. Leistungsoptimierung und Best Practices umfassen die Verwendung geeigneter Signaturalgorithmen, das Einstellen von Gültigkeitsperioden angemessen.

Dieses Tutorial zeigt, wie XML -Dokumente mit PHP effizient verarbeitet werden. XML (Extensible Markup-Sprache) ist eine vielseitige textbasierte Markup-Sprache, die sowohl für die Lesbarkeit des Menschen als auch für die Analyse von Maschinen entwickelt wurde. Es wird üblicherweise für die Datenspeicherung ein verwendet und wird häufig verwendet

Eine Zeichenfolge ist eine Folge von Zeichen, einschließlich Buchstaben, Zahlen und Symbolen. In diesem Tutorial wird lernen, wie Sie die Anzahl der Vokale in einer bestimmten Zeichenfolge in PHP unter Verwendung verschiedener Methoden berechnen. Die Vokale auf Englisch sind a, e, i, o, u und sie können Großbuchstaben oder Kleinbuchstaben sein. Was ist ein Vokal? Vokale sind alphabetische Zeichen, die eine spezifische Aussprache darstellen. Es gibt fünf Vokale in Englisch, einschließlich Großbuchstaben und Kleinbuchstaben: a, e, ich, o, u Beispiel 1 Eingabe: String = "TutorialPoint" Ausgabe: 6 erklären Die Vokale in der String "TutorialPoint" sind u, o, i, a, o, ich. Insgesamt gibt es 6 Yuan

Statische Bindung (statisch: :) implementiert die späte statische Bindung (LSB) in PHP, sodass das Aufrufen von Klassen in statischen Kontexten anstatt Klassen zu definieren. 1) Der Analyseprozess wird zur Laufzeit durchgeführt.

Was sind die magischen Methoden von PHP? Zu den magischen Methoden von PHP gehören: 1. \ _ \ _ Konstrukt, verwendet, um Objekte zu initialisieren; 2. \ _ \ _ Destruct, verwendet zur Reinigung von Ressourcen; 3. \ _ \ _ Call, behandeln Sie nicht existierende Methodenaufrufe; 4. \ _ \ _ GET, Implementieren Sie den dynamischen Attributzugriff; 5. \ _ \ _ Setzen Sie dynamische Attributeinstellungen. Diese Methoden werden in bestimmten Situationen automatisch aufgerufen, wodurch die Code -Flexibilität und -Effizienz verbessert werden.
