Schlüsselpunkte
Dieser Artikel wurde von Christopher Pitt überprüft. Vielen Dank an alle SitePoint -Peer -Rezensenten, die SitePoint -Inhalte perfekt gemacht haben!
PHP -Entwickler scheinen die Parallelität selten zu nutzen. Die Einfachheit der synchronen Programmierung mit einem Thread ist wirklich attraktiv, aber manchmal kann die Verwendung einer kleinen Parallelität zu einigen lohnenden Leistungsverbesserungen führen.
In diesem Artikel werden wir lernen, wie man Threads in PHP mithilfe der PThreads -Erweiterung implementiert. Dies erfordert die Installation der ZTS-Version (Zend-Thread-Safe) -Version von PHP 7.x sowie die Installation von PThreads v3. (Zum Zeitpunkt des Schreibens müssen die Benutzer von PHP 7.1 aus dem Master -Zweig des PThreads Repo installiert werden. Weitere Informationen zum Erstellen von Erweiterungen von Drittanbietern aus Quelle finden Sie in diesem Artikel.)
Eine schnelle Erklärung: PTHREADS V2 richtet sich an PHP 5.x, nicht mehr unterstützt;
Vielen Dank an Joe Watkins (Schöpfer der PThreads -Erweiterung) für das Korrekturlesen und die Verbesserung meines Beitrags!
Bevor wir fortfahren, möchte ich zunächst die Situation erklären, in der Sie nicht (und nicht ) die Erweiterung pThreads nicht verwenden sollten.
In PTHREADS V2 wird nicht empfohlen, PThreads in einer Webserverumgebung zu verwenden (d. H. In einem FCGI -Prozess). Ausgehend von PThreads V3 wird dieser Vorschlag erzwungen, sodass Sie ihn überhaupt nicht in einer Webserverumgebung verwenden können. Zwei Hauptgründe dafür sind:Deshalb sind Threads in dieser Umgebung keine gute Lösung. Wenn Sie nach einer Lösung für Threads als Blockierung von IOs suchen (z. B. HTTP -Anforderungen), lassen Sie mich die Richtung von asynchronem Programmieren verweisen, die durch Frameworks wie AMP erreicht werden können. SitePoint hat einige hervorragende Artikel zu diesem Thema veröffentlicht (z. B. das Schreiben von asynchronen Bibliotheken und das Ändern von Minecraft mit PHP), wenn Sie interessiert sind.
Kommen Sie wieder auf den Punkt, gehen wir direkt zum Thema!
Manchmal möchten Sie einmalige Aufgaben auf multi-thread-Weise erledigen (z. B. einige IO-gebundene Aufgaben). In diesem Fall können Sie die Thread -Klasse verwenden, um einen neuen Thread zu erstellen und einige Arbeitseinheiten in diesem separaten Thread auszuführen.
Beispiel:
$task = new class extends Thread { private $response; public function run() { $content = file_get_contents("http://google.com"); preg_match("~<title>(.+)</title>~", $content, $matches); $this->response = $matches[1]; } }; $task->start() && $task->join(); var_dump($task->response); // string(6) "Google"
oben ist die Run -Methode die Arbeitseinheit, die wir in einem neuen Thread ausführen werden. Wenn Thread :: Start aufgerufen wird, wird ein neuer Thread generiert und die Run -Methode aufgerufen. Anschließend werden wir dem generierten Thread in den Haupt Thread (über Thread :: join) zurückversetzt, der blockiert, bis der einzelnen Thread die Ausführung abgeschlossen hat. Dies stellt sicher, dass die Aufgabe die Ausführung abgeschlossen hat, bevor versucht wird, das Ergebnis auszugeben (in $ Task & gt; Antwort gespeichert).
Die Verantwortlichkeiten zur Kontaminierung der Klasse mit Thread-bezogenen Logik (einschließlich der Definition von Laufmethoden) sind möglicherweise nicht ideal. Wir können diese Klassen isolieren, indem wir die Thread -Klasse erweitern und sie dann in anderen Threads ausführen:
class Task extends Threaded { public $response; public function someWork() { $content = file_get_contents('http://google.com'); preg_match('~<title>(.+)</title>~', $content, $matches); $this->response = $matches[1]; } } $task = new Task; $thread = new class($task) extends Thread { private $task; public function __construct(Threaded $task) { $this->task = $task; } public function run() { $this->task->someWork(); } }; $thread->start() && $thread->join(); var_dump($task->response);
Jede Klasse, die in einem separaten Thread ausgeführt werden muss, muss die Thread -Klasse auf irgendeine Weise erweitern. Dies liegt daran, dass es die erforderlichen Funktionen für die Ausführung in verschiedenen Threads sowie die implizite Sicherheits- und nützliche Schnittstellen (für die Ressourcensynchronisation usw.) bietet. Lassen Sie uns schnell die von pthreads entlarvte Klassenhierarchie verstehen:
Wir haben bereits die Grundlagen des Thread- und Thread -Klassen gelernt.
<code>Threaded (implements Traversable, Collectable) Thread Worker Volatile Pool</code>
Es ist teuer, einen neuen Thread zu starten, damit jede Aufgabe parallelisiert wird. Dies liegt daran, dass PTHREADS eine gemeinsame staatenlose Architektur annehmen muss, um Threads in PHP zu implementieren. Dies bedeutet, dass der gesamte Ausführungskontext (einschließlich jeder Klasse, jeder Schnittstelle, des Attributs und der Funktion) der aktuellen Instanz des PHP -Interpreter für jeden erstellten Thread kopiert werden muss. Da dies einen erheblichen Leistungsauswirkungen haben kann, sollten Threads immer so weit wie möglich wiederverwendet werden. Es gibt zwei Möglichkeiten, Threads wiederzuverwenden: Verwendung von Arbeitern oder Pool.
Die Arbeiterklasse wird verwendet, um eine Reihe von Aufgaben synchron in einem anderen Thread auszuführen. Dies geschieht durch das Erstellen einer neuen Worker -Instanz (so wird ein neuer Thread erstellt) und dann Aufgaben auf diesen separaten Thread (über Worker :: Stack) stapeln.
Dies ist ein einfaches Beispiel:
$task = new class extends Thread { private $response; public function run() { $content = file_get_contents("http://google.com"); preg_match("~<title>(.+)</title>~", $content, $matches); $this->response = $matches[1]; } }; $task->start() && $task->join(); var_dump($task->response); // string(6) "Google"
Ausgabe:
Die oben genannten Stapel 15 Aufgaben auf das neue $ Worker -Objekt über Worker :: Stack und bearbeiten Sie sie dann in Stapelreihenfolge. Wie oben gezeigt, wird die Arbeiter :: Sammelmethode verwendet, um Aufgaben zu bereinigen, nachdem die Aufgabe die Ausführung abgeschlossen hat. Indem wir es in der WHOR -Schleife verwenden, blockieren wir den Hauptfaden, bis alle gestapelten Aufgaben ausgeführt und gereinigt wurden, und dann lösen wir Arbeiter :: Shutdown. Das vorzeitige Schließen des Arbeiters (d. H. Während die Aufgaben noch ausgeführt werden müssen) blockiert den Hauptfaden, bis alle Aufgaben ausgeführt werden - die Aufgaben werden einfach nicht gesammelt (und Speicherlecks verursachen).
DieDie Arbeiterklasse bietet einige andere Aufgaben-stapelbezogene Methoden, darunter Worker :: Unstack für das Löschen der ältesten Stapelartikel und Worker :: Getstacked für die Ausführung der Anzahl der Elemente auf dem Stapel. Der Stapel eines Arbeiters rettet nur die Aufgaben, die ausgeführt werden sollen. Sobald die Aufgabe im Stapel ausgeführt wurde, wird sie gelöscht und auf einen anderen (internen) Stapel für die Müllsammlung gelöscht (mit Worker :: sammeln).
Eine andere Möglichkeit, Threads bei der Ausführung vieler Aufgaben wiederzuverwenden, besteht darin, Threadpools (über die Poolklasse) zu verwenden. Ein Thread -Pool wird von einer Reihe von Arbeitern angetrieben, damit die Aufgaben gleichzeitig ausgeführt werden können, wobei der Parallelitätsfaktor (die Anzahl der Fäden, die der Pool ausführt) zum Zeitpunkt der Erstellung von Pools angegeben wird. Passen wir das obige Beispiel an, um den Arbeiterpool zu verwenden:
Ausgabe:
class Task extends Threaded { public $response; public function someWork() { $content = file_get_contents('http://google.com'); preg_match('~<title>(.+)</title>~', $content, $matches); $this->response = $matches[1]; } } $task = new Task; $thread = new class($task) extends Thread { private $task; public function __construct(Threaded $task) { $this->task = $task; } public function run() { $this->task->someWork(); } }; $thread->start() && $thread->join(); var_dump($task->response);
Es gibt einige signifikante Unterschiede zwischen der Verwendung von Pools und der Verwendung von Arbeiterprogrammen. Zunächst müssen Pools nicht manuell gestartet werden, sie beginnen, Aufgaben auszuführen, sobald sie verfügbar sind. Zweitens senden wir die Aufgaben
Als gute Praxis sollten Sie nach dem Ausfüllen von Aufgaben immer Aufgaben für Arbeiterprogramme und Pools sammeln und manuell schließen. Über die Thread -Klasse erstellte Themen sollten ebenfalls wieder in den Erstellungs -Thread angeschlossen werden.
Die letzte Klasse, die eingeführt wird, ist flüchtig - ein Neuzugang zu PThreads v3. Die Invarianz ist zu einem wichtigen Konzept in Phreads geworden, da die Leistung ohne sie stark verschlechtert wird. Standardmäßig sind Eigenschaften der Thread -Klasse, die selbst ein Thread -Objekt sind, jetzt unveränderlich und kann daher nach der Erstzuweisung nicht neu zugewiesen werden. Jetzt ist es eher geneigt, diese Eigenschaften explizit zu mutieren, und es kann weiterhin mit der neuen volatilen Klasse erfolgen.
Schauen wir uns schnell ein Beispiel an, um die neue Invarianzbeschränkung zu demonstrieren:
$task = new class extends Thread { private $response; public function run() { $content = file_get_contents("http://google.com"); preg_match("~<title>(.+)</title>~", $content, $matches); $this->response = $matches[1]; } }; $task->start() && $task->join(); var_dump($task->response); // string(6) "Google"
Andererseits ist die Gewinde Eigenschaft der flüchtigen Klasse veränderlich:
class Task extends Threaded { public $response; public function someWork() { $content = file_get_contents('http://google.com'); preg_match('~<title>(.+)</title>~', $content, $matches); $this->response = $matches[1]; } } $task = new Task; $thread = new class($task) extends Thread { private $task; public function __construct(Threaded $task) { $this->task = $task; } public function run() { $this->task->someWork(); } }; $thread->start() && $thread->join(); var_dump($task->response);
Wir können sehen, dass die volatile Klasse die Invarianz, die von der übergeordneten Klasse -Thread -Klasse erzwungen wird, überschreibt, um die Eigenschaft mit Thread -Thread -Eigenschaften zu ermöglichen.
Über Variabilität und volatile Klassen müssen ein weiteres letztes grundlegendes Thema eingeführt werden - Arrays. Wenn ein Array einer Eigenschaft der Thread -Klasse zugeordnet ist, wird das Array in Phreads automatisch in ein flüchtiges Objekt gegossen. Dies liegt daran, dass es nicht sicher ist, Arrays aus mehreren Kontexten in PHP zu manipulieren.
Schauen wir uns schnell ein Beispiel an, um besser zu verstehen:
<code>Threaded (implements Traversable, Collectable) Thread Worker Volatile Pool</code>
Wir können sehen, dass flüchtige Objekte wie Arrays behandelt werden können, da sie Untersetzer ([]) für Array-basierte Operationen (wie oben gezeigt) unterstützen. Die volatile Klasse wird jedoch nicht durch allgemeine Array-basierte Funktionen wie Array_Pop und Array_Shift unterstützt. Stattdessen bietet uns die Thread-Klasse diese Vorgänge als integrierte Methoden.
als Demonstration:
class Task extends Threaded { private $value; public function __construct(int $i) { $this->value = $i; } public function run() { usleep(250000); echo "Task: {$this->value}\n"; } } $worker = new Worker(); $worker->start(); for ($i = 0; $i < 15; $i++) { $worker->stack(new Task($i)); } while ($worker->collect()); $worker->shutdown();
Andere unterstützte Operationen umfassen Threaded :: Chunk und Threaded :: Merge.
Das letzte Thema, das in diesem Artikel eingeführt wird, ist die Synchronisation in Pthreads. Die Synchronisation ist eine Technologie, mit der der Steuerzugriff auf gemeinsam genutzte Ressourcen gesteuert werden kann.
Implementieren wir zum Beispiel einen einfachen Zähler:
class Task extends Threaded { private $value; public function __construct(int $i) { $this->value = $i; } public function run() { usleep(250000); echo "Task: {$this->value}\n"; } } $pool = new Pool(4); for ($i = 0; $i < 15; $i++) { $pool->submit(new Task($i)); } while ($pool->collect()); $pool->shutdown();
Wenn keine Synchronisation verwendet wird, ist der Ausgang nicht deterministisch. Mehrere Threads schreiben in eine einzelne Variable, ohne den Zugriff zu kontrollieren, zu verlorenen Updates.
Lassen Sie uns dies korrigieren, indem wir die Synchronisation hinzufügen, damit wir die richtige Ausgabe 20:
erhaltenclass Task extends Threaded // a Threaded class { public function __construct() { $this->data = new Threaded(); // $this->data is not overwritable, since it is a Threaded property of a Threaded class } } $task = new class(new Task()) extends Thread { // a Threaded class, since Thread extends Threaded public function __construct($tm) { $this->threadedMember = $tm; var_dump($this->threadedMember->data); // object(Threaded)#3 (0) {} $this->threadedMember = new StdClass(); // invalid, since the property is a Threaded member of a Threaded class } };
Synchronisierungscodeblöcke können auch mit Threaded :: Wait and Threaded :: Benachrichtigung (und Threaded :: musifyone) funktionieren.
Folgendes sind die verschachtelten Inkremente von zwei Synchronen während der Schleifen:
class Task extends Volatile { public function __construct() { $this->data = new Threaded(); $this->data = new StdClass(); // valid, since we are in a volatile class } } $task = new class(new Task()) extends Thread { public function __construct($vm) { $this->volatileMember = $vm; var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {} // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class $this->volatileMember = new StdClass(); } };
Sie haben möglicherweise festgestellt, dass um den Anruf zum Threaded zusätzliche Bedingungen hinzugefügt werden:: Warten Sie. Diese Bedingungen sind kritisch, da sie nur zu, dass synchrone Rückrufe wiederhergestellt werden, wenn die Benachrichtigung und angeben, dass die Bedingung wahr ist. Dies ist wichtig, da Benachrichtigungen von außerhalb des Threaded :: Benachrichtigungsaufrufs kommen können. Wenn der Aufruf zum Thread :: Warten nicht in der Bedingung enthalten ist, sind wir anfällig für den falschen Weckruf , wodurch der Code unvorhersehbar ist.
Wir haben fünf Klassen (Gewinde, Gewinde, Arbeiter, volatile und Pool) gesehen, die mit Phreads geliefert werden, einschließlich der Einführung der Verwendung jeder Klasse. Wir haben uns auch das neue Konzept der Invarianz in Phreads angesehen und die von ihnen unterstützten Synchronisierungsfunktionen schnell angesehen. Mit diesen Grundlagen können wir jetzt anfangen, PTHREADS auf einige praktische Anwendungsfälle anzuwenden! Dies wird Gegenstand unseres nächsten Beitrags sein.
gleichzeitig können Sie Ihre Gedanken im Kommentarbereich unten im Kommentarbereich hinterlassen, wenn Sie Bewerbungsideen für PTHREADS haben!
Um pthreads in PHP zu verwenden, müssen Sie Kenntnisse über PHP und objektorientierte Programmierung haben. Sie müssen außerdem ZTS -fähige PHP (Zend Thread Safety) installieren. Pthreads ist nicht in einer Standard-PHP-Installation verfügbar. Sie können überprüfen, ob Ihre PHP -Installation ZTs aktiviert ist, indem Sie den Befehl "PHP -i | Grep" Thread Safety "" im Terminal ausführen. Wenn es "Thread Safety = & gt; aktiviert" zurückgibt, können Sie PThreads verwenden.
Um Pthreads zu installieren, müssen Sie PECL verwenden, der PHP -Erweiterungs -Community -Bibliothek. Stellen Sie zunächst sicher, dass ZTS -Aktiva -PHP aktiviert ist. Führen Sie dann in Ihrem Terminal den Befehl "Pecl installieren pThreads" aus. Wenn die Installation erfolgreich ist, müssen Sie Ihrer Php.ini -Datei die Zeile "Erweiterung = pthreads.so" hinzufügen. Dadurch wird die Pthreads -Erweiterung jedes Mal geladen, wenn PHP ausgeführt wird.
Um einen neuen Thread zu erstellen, müssen Sie eine Klasse definieren, die die von PTHREADS bereitgestellte Thread -Klasse erweitert. In dieser Klasse überschreiben Sie die Run () -Methode, die der Code ist, der in einem neuen Thread ausgeführt wird. Sie können dann eine Instanz dieser Klasse erstellen und seine Start () -Methode aufrufen, um einen neuen Thread zu starten.
pthreads liefert die Thread -Klasse zum Teilen von Daten zwischen Threads. Sie können eine neue Instanz dieser Klasse erstellen und an Ihren Thread weitergeben. Alle in diesem Objekt festgelegten Eigenschaften werden sicher zwischen Threads geteilt.
Fehlerbehebung in PThreads ähnelt dem Fehlerhandling in Standard -PHP. Sie können den Try-Catch-Block verwenden, um Ausnahmen zu fangen. Beachten Sie jedoch, dass jeder Thread seinen eigenen Umfang hat. Ausnahmen in einem Thread beeinflussen daher andere Threads nicht.
pthreads ist mit PHP -Frameworks wie Laravel oder Symfony unvereinbar. Dies liegt daran, dass diese Frameworks nicht so konzipiert sind, dass er mit Thread-Sicherheit ist. Wenn Sie in diesen Frameworks eine parallele Verarbeitung durchführen müssen, sollten Sie andere Techniken wie Warteschlangen oder asynchrone Aufgaben verwenden.
Debuggen von PHP -Skripten mit Pthreads können eine Herausforderung sein, da jeder Thread in seinem eigenen Kontext ausgeführt wird. Sie können jedoch Standard -Debugging -Techniken wie Aufzeichnung oder Ausgabe von Daten in die Konsole verwenden. Sie können auch PHP -Debugger wie Xdebug verwenden, sind sich jedoch bewusst, dass nicht alle Debugger -Multithread -Anwendungen unterstützen.
pthreads wird in Webserverumgebungen nicht empfohlen. Es ist für CLI -Skripte (Befehlszeilenschnittstellen) ausgelegt. Die Verwendung von pthreads in einer Webserverumgebung kann zu unvorhersehbaren Ergebnissen führen und ist häufig unsicher.
Um einen laufenden Thread zu stoppen, können Sie die von PThreads bereitgestellte Kill () -Methode verwenden. Diese Methode sollte jedoch mit Vorsicht verwendet werden, da sie zu unvorhersehbaren Ergebnissen führen kann, wenn der Faden in Betrieb ist. Es ist normalerweise am besten, Ihre Fäden so zu gestalten, dass sie ihre Aufgaben sauber erledigen können.
Ja, es gibt mehrere Alternativen zu Pthreads für die parallele Programmierung in PHP. Dazu gehören Gabeln, eine PECL -Erweiterung, die Schnittstellen für das Erstellen und Verwalten von untergeordneten Prozessen sowie eine in PHP 7.2 eingeführte native PHP -Erweiterung bietet, die eine einfachere und sicherere parallele Programmierschnittstelle bietet.
Das obige ist der detaillierte Inhalt vonParallele Programmierung mit PThreads in PHP - den Grundlagen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!