Heim > Backend-Entwicklung > PHP-Tutorial > Detaillierte Einführung in Coroutinen in PHP (Code)

Detaillierte Einführung in Coroutinen in PHP (Code)

不言
Freigeben: 2023-04-04 07:24:02
Original
3912 Leute haben es durchsucht

In diesem Artikel wird zunächst das Konzept des Generators vorgestellt, wobei der Schwerpunkt auf der Nutzung des Ertrags und der Schnittstelle des Generators liegt. Der Coroutine-Teil erläutert kurz die Prinzipien von Coroutinen und Dinge, auf die bei der PHP-Coroutine-Programmierung geachtet werden sollte.

PHP hat seit 5.5 einen Generator (Generator) eingeführt, auf dessen Grundlage eine Coroutine-Programmierung realisiert werden kann. Dieser Artikel beginnt mit einem Überblick über Generatoren und geht dann zur Coroutine-Programmierung über.

Ertrag und Generator

Generator

Ein Generator ist ein Datentyp, der die Iteratorschnittstelle implementiert. Die Generatorinstanz kann nicht über new abgerufen werden, und es gibt keine statische Methode zum Abrufen der Generatorinstanz. Die einzige Möglichkeit, eine Generatorinstanz zu erhalten, besteht darin, die Generatorfunktion aufzurufen (eine Funktion, die das Schlüsselwort yield enthält). Durch den direkten Aufruf der Generatorfunktion wird ein Generatorobjekt zurückgegeben, und der Code innerhalb der Funktion beginnt mit der Ausführung, wenn der Generator ausgeführt wird.

Gehen Sie zunächst zum Code, um Ertrag und Generator intuitiv zu erleben:

# generator1.php
function foo() {
    exit('exit script when generator runs.');
    yield;
}

$gen = foo();
var_dump($gen);
$gen->current();

echo 'unreachable code!';

# 执行结果
object(Generator)#1 (0) {
}
exit script when generator runs.
Nach dem Login kopieren

fooDie Funktion enthält das Schlüsselwort yield und wandelt sich in eine Generatorfunktion um. Der Aufruf von foo führt keinen Code im Funktionskörper aus, sondern gibt stattdessen eine Generatorinstanz zurück. Nachdem der Generator ausgeführt wurde, wird der Code in der Funktion foo ausgeführt und das Skript beendet.

Wie der Name schon sagt, können Generatoren zur Generierung von Daten verwendet werden. Die Art und Weise, wie Daten generiert werden, unterscheidet sich lediglich von anderen Funktionen: Der Generator gibt Daten über yield statt über return zurück. Nachdem yield Daten zurückgegeben hat, wird die Generatorfunktion nicht zerstört, sondern pausiert lediglich ihren Betrieb. Sie können die Ausführung in der Zukunft fortsetzen; der Generator wird einmal ausgeführt und gibt (nur) einen Datenwert zurück. Wenn der Generator nicht aufgerufen wird, um Daten zu erhalten Der Code im Generator bleibt jedes Mal stehen, beispielsweise so. So sehen die vom Generator generierten Daten aus.

Der Generator implementiert die Iterator-Schnittstelle. Sie können die foreach Schleife oder die manuelle current/next/valid verwenden, um die Generatordaten zu erhalten. Der folgende Code demonstriert die Datengenerierung und -durchquerung:

# generator2.php
function foo() {
  # 返回键值对数据
  yield "key1" => "value1";
  $count = 0;
  while ($count < 5) {
    # 返回值,key自动生成
    yield $count;
    ++ $count;
  }
  # 不返回值,相当于返回null
  yield;
}

# 手动获取生成器数据
$gen = foo();
while ($gen->valid()) {
  fwrite(STDOUT, "key:{$gen->key()}, value:{$gen->current()}\n");
  $gen->next();
}

# foreach 遍历数据
fwrite(STDOUT, "\ndata from foreach\n");
foreach (foo() as $key => $value) {
    fwrite(STDOUT, "key:$key, value:$value\n");
}
Nach dem Login kopieren

yield

yieldDas Schlüsselwort ist der Kern des Generators, der es gewöhnlichen Funktionen ermöglicht, sich in Generatorfunktionen zu differenzieren (zu entwickeln). yield bedeutet „aufgeben“. Wenn das Programm die yield-Anweisung erreicht, wird die Ausführung angehalten, die CPU wird aufgegeben und die Kontrolle wird an den Aufrufer zurückgegeben, und die Ausführung wird beim nächsten Mal am Unterbrechungspunkt fortgesetzt es wird ausgeführt. Wenn die Kontrolle an den Aufrufer zurückgegeben wird, kann die yield-Anweisung den Wert an den Aufrufer zurückgeben. generator2.phpDas Skript zeigt drei Formen von Yield-Rückgabewerten:

  1. yield $key => $value: gibt den Schlüssel und den Wert der Daten zurück; >

    Yield $value: Gibt Daten zurück, Schlüssel wird vom System zugewiesen
  2. Yield: Gibt Nullwert zurück, Schlüssel wird vom System zugewiesen; 🎜>
  3. Erlauben Sie der Funktion, die Funktion anzuhalten, die Ausführung jederzeit fortzusetzen und Daten an den Aufrufer zurückzugeben. Wenn externe Daten benötigt werden, um die Ausführung fortzusetzen, wird diese Arbeit von der

    -Funktion des Generators bereitgestellt: Die Variable, die auf der linken Seite von

    erscheint, erhält den von
  4. übergebenen Wert. Schauen wir uns ein häufiges
-Funktionsverwendungsbeispiel an:

function logger(string $filename) {
  $fd = fopen($filename, 'w+');
  while($msg = yield) {
    fwrite($fd, date('Y-m-d H:i:s') . ':' . $msg . PHP_EOL);
  }
  fclose($fd);
}

$logger = logger('log.txt');
$logger->send('program starts!');
// do some thing
$logger->send('program ends!');
Nach dem Login kopieren
yieldsend ermöglicht die bidirektionale Datenkommunikation zwischen Generatoren und der Außenwelt: yield gibt Daten zurück; send bietet Unterstützung für fortlaufende Betriebsdaten . Da send es dem Generator ermöglicht, die Ausführung fortzusetzen, ähnelt dieses Verhalten der -Schnittstelle des Iterators und

entspricht send. yieldsendAndere sendnextnextsend(null)

-Ausdrücke sind vor PHP7 nicht gültig und müssen in Klammern gesetzt werden:

;

  1. Die PHP5-Generatorfunktion kann keinen $string = yield $data;-Wert haben. Nach PHP7 kann sie einen Wert zurückgeben und den zurückgegebenen Wert über $string = (yield $data) des Generators abrufen.

  2. PHP7 fügt eine neue return-Syntax hinzu, um die Generatordelegierung zu implementieren. getReturn

  3. Der Generator ist ein Einweg-Iterator und

    kann nach dem Start nicht mehr aufgerufen werden. yield from

  4. Zusammenfassung

    rewind Im Vergleich zu anderen Iteratoren zeichnen sich Generatoren durch einen geringen Leistungsaufwand und eine einfache Codierung aus. Seine Rolle spiegelt sich hauptsächlich in drei Aspekten wider:

Datengenerierung (Produzent), Datenrückgabe durch Ertrag

    Datenverbrauch (Verbraucher), verbrauchen die per send gesendeten Daten;
  1. implementiert die Coroutine.
  2. In Bezug auf Generatoren und die grundlegende Verwendung in PHP wird empfohlen, den Blogbeitrag des Chefs von
  3. 2gua

    zu lesen: PHP Generator, interessant und leicht zu verstehen.

  4. Coroutine-Programmierung

Coroutine ist eine Unterroutine, die jederzeit unterbrochen und fortgesetzt werden kann. Das Schlüsselwort ermöglicht der Funktion diese Fähigkeit, sodass sie für die Coroutine-Programmierung verwendet werden kann. .

Prozesse, Threads und Coroutinen

Threads gehören zu Prozessen und ein Prozess kann mehrere Threads haben. Ein Prozess ist die kleinste Einheit für die Computerzuweisung von Ressourcen, und ein Thread ist die kleinste Einheit für die Computerplanung und -ausführung. Sowohl Prozesse als auch Threads werden vom Betriebssystem geplant.

yieldCoroutinen können als „Benutzermodus-Threads“ betrachtet werden, die Benutzerprogramme zur Implementierung der Planung erfordern. Threads und Prozesse werden vom Betriebssystem so geplant, dass sie abwechselnd auf „präventive“ Weise ausgeführt werden, und Coroutinen überlassen aktiv die CPU, um abwechselnd auf „negative“ Weise ausgeführt zu werden. Coroutinen sind sehr leicht, der Coroutine-Wechsel erfordert keinen Thread-Wechsel und die Ausführungseffizienz ist hoch. Je größer die Anzahl, desto besser können die Vorteile von Coroutinen widergespiegelt werden.

Generatoren und Coroutinen

Die vom Generator implementierte Coroutine ist eine stapellose Coroutine, das heißt, die Generatorfunktion verfügt nur über einen Funktionsrahmen, der zur Laufzeit zur Ausführung an den Stapel des Aufrufers angehängt wird. Im Gegensatz zur leistungsstarken Stack-Coroutine kann der Generator die Richtung des Programms nicht steuern, nachdem es angehalten wurde, und kann die Kontrolle nur passiv an den Aufrufer zurückgeben. Der Generator kann sich nur selbst unterbrechen, nicht die gesamte Coroutine. Der Vorteil des Generators besteht natürlich darin, dass er sehr effizient ist (Sie müssen nur den Programmzähler speichern, wenn Sie pausieren) und einfach zu implementieren ist.

Coroutine-Programmierung

Apropos Coroutine-Programmierung in PHP: Ich glaube, die meisten Leute haben diesen von Bruder Niao nachgedruckten (übersetzten) Blog-Beitrag bereits gelesen: Mit Coroutinen mehrere Funktionen in der PHP-Aufgabenplanung erreichen. Der ursprüngliche Autor Nikic ist der Kernentwickler von PHP, der Initiator und Implementierer der Generatorfunktion. Wenn Sie mehr über Generatoren und die darauf basierende Coroutine-Programmierung erfahren möchten, sind Nikics RFC zu Generatoren und die Artikel auf der Website von Niaoge ein Muss.

Schauen wir uns zunächst an, wie Generator-basierte Coroutinen funktionieren: Coroutinen arbeiten kollaborativ, das heißt, Coroutinen geben aktiv die CPU ab, um eine abwechselnde Ausführung mehrerer Aufgaben zu erreichen (also gleichzeitiges Multitasking, aber nicht parallel A). Der Generator kann als Coroutine betrachtet werden. Wenn die Anweisung yield ausgeführt wird, wird die CPU-Steuerung an den Aufrufer zurückgegeben und der Aufrufer führt weiterhin andere Coroutinen oder andere Codes aus.

Schauen wir uns die Schwierigkeit an, den Blog von Bruder Bird zu verstehen. Coroutinen sind sehr leichtgewichtig und Tausende von Coroutinen (Generatoren) können gleichzeitig in einem System existieren. Das Betriebssystem plant keine Coroutinen und die Arbeit, die Coroutinenausführung zu organisieren, obliegt den Entwicklern. Einige Leute verstehen den Coroutine-Teil des Artikels von Bruder Niao nicht, weil er sagt, dass es wenig Coroutine-Programmierung gibt (das Schreiben von Coroutinen bedeutet hauptsächlich das Schreiben von Generatorfunktionen), aber sie verbringen viel Zeit mit der Implementierung eines Coroutine-Schedulers (Scheduler oder Kernel): Simuliert das Betriebssystem und führt eine faire Planung für alle Coroutinen durch. Der allgemeine Gedanke bei der PHP-Entwicklung ist: Ich habe diese Codes geschrieben und die PHP-Engine ruft meine Codes auf, um die erwarteten Ergebnisse zu erzielen. Coroutine-Programmierung erfordert nicht nur das Schreiben von Code, um die Arbeit zu erledigen, sondern auch das Schreiben von Code, um diesen Codes anzuweisen, wann sie arbeiten sollen. Wenn Sie die Denkweise des Autors nicht gut verstehen, wird es natürlich schwieriger sein, sie zu verstehen. Es muss selbst geplant werden, was einen Nachteil der Generator-Coroutine im Vergleich zur nativen Coroutine (Async/Wait-Form) darstellt.

Da wir nun wissen, was Coroutine ist, wofür kann sie verwendet werden? Die Coroutine gibt die CPU selbstständig auf, um zusammenzuarbeiten und die CPU effizient zu nutzen. Natürlich sollte der Zeitpunkt zum Aufgeben liegen, wenn das Programm blockiert ist. Wo wird das Programm blockieren? Benutzermoduscode blockiert selten und wird hauptsächlich durch Systemaufrufe verursacht. Die meisten Systemaufrufe sind E/A-Aufrufe, daher ist das Hauptanwendungsszenario von Coroutinen die Netzwerkprogrammierung. Um eine hohe Leistung und Parallelität des Programms zu gewährleisten, sollte das Programm asynchron ausgeführt werden und nicht blockieren. Da die asynchrone Ausführung Benachrichtigungen und Rückrufe erfordert, kann das Schreiben von Rückruffunktionen das Problem der „Rückrufhölle“ nicht vermeiden: Die Lesbarkeit des Codes ist schlecht und der Programmausführungsprozess ist auf die Schichten von Rückruffunktionen verteilt. Es gibt zwei Möglichkeiten, die Callback-Hölle zu lösen: Promise und Coroutinen. Coroutinen können Code synchron schreiben und werden in der Hochleistungsnetzwerkprogrammierung (IO-intensiv) empfohlen.

Werfen wir einen Blick zurück auf die Coroutine-Programmierung in PHP. In PHP wird die Coroutine-Programmierung auf Basis von Generatoren implementiert. Es wird empfohlen, Coroutine-Frameworks wie RecoilPHP und Amp zu verwenden. Diese Frameworks verfügen bereits über geschriebene Scheduler. Wenn Sie eine Generatorfunktion direkt darauf entwickeln, plant der Kernel automatisch die Ausführung (wenn Sie möchten, dass eine Funktion in einem Coroutine-Modus ausgeführt wird, fügen Sie einfach yield in den Funktionskörper ein). Wenn Sie die yield-Methode nicht für die Coroutine-Programmierung verwenden möchten, empfehlen wir swoole oder sein abgeleitetes Framework, mit dem Sie ein Golang-ähnliches Coroutine-Programmiererlebnis erzielen und gleichzeitig die Entwicklungseffizienz von PHP genießen können.

Wenn Sie die ursprüngliche PHP-Coroutine-Programmierung verwenden möchten, ist ein Scheduler ähnlich dem im Blog von Niao Ge unerlässlich. Der Scheduler plant die Ausführung der Coroutine. Nachdem die Coroutine unterbrochen wurde, kehrt die Kontrolle zum Scheduler zurück. Daher sollte sich der Scheduler immer in der Hauptschleife (Ereignisschleife) befinden, d. h. wenn die CPU die Coroutine nicht ausführt, sollte sie den Scheduler-Code ausführen. Bei der Ausführung ohne Coroutine sollte sich der Scheduler selbst blockieren, um eine CPU-Belastung zu vermeiden (Niao Ges Blog verwendet den integrierten Systemaufruf select), auf das Eintreffen des Ereignisses warten und dann die entsprechende Coroutine ausführen. Während der Ausführung des Programms sollte die Coroutine, mit Ausnahme der Blockierung durch den Scheduler, während der Ausführung keine blockierenden APIs aufrufen.

Zusammenfassung

Bei der Coroutine-Programmierung besteht die Hauptaufgabe von yield darin, die Kontrolle zu übertragen, ohne sich um den Rückgabewert zu kümmern (im Grunde wird der von yield zurückgegebene Wert beim nächsten Mal ausgeführt direkt send wenn es soweit ist). Der Schwerpunkt sollte auf dem Zeitpunkt der Kontrollübertragung und der Funktionsweise der Coroutine liegen.

Ein weiterer zu beachtender Punkt ist, dass Coroutinen wenig mit Asynchronität zu tun haben, sondern auch von der Unterstützung der Betriebsumgebung abhängt. In der herkömmlichen PHP-Betriebsumgebung wird Promise/Coroutine immer noch synchron blockiert, selbst wenn Promise/Coroutine verwendet wird. Egal wie großartig das Coroutine-Framework ist, sleep es wird überhaupt nicht funktionieren. Als Analogie: Auch wenn JavaScript keine Promise/Async-Technologien verwendet, ist es asynchron und nicht blockierend.

Durch Generatoren und Promise kann eine Coroutine-Programmierung ähnlich wie await implementiert werden. Auf Github gibt es viele relevante Codes, die in diesem Artikel nicht behandelt werden.

Verwandte Empfehlungen:

$_SERVER in PHP Detaillierte Einführung

Detaillierte Einführung in Output_Buffering in PHP, Outputbuffering_PHP-Tutorial

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in Coroutinen in PHP (Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage