Einfache PHP-Multithreading-Implementierung

Multithreading in PHP fehlt "out of the box", daher wurden sehr viele Optionen für seine Implementierung erfunden, darunter die Erweiterungen pthreads , AzaThread (CThread) und sogar einige der eigenen Entwicklungen der PHP-Entwickler.

Der Hauptnachteil für mich waren zu viele „Schnickschnack“ für diese Lösungen - es besteht nicht immer die Notwendigkeit, Informationen zwischen Threads und dem übergeordneten Prozess auszutauschen oder Ressourcen zu sparen. Es sollte immer die Möglichkeit geben, das Problem schnell und kostengünstig zu lösen.

Ich möchte im Voraus reservieren, dass in diesem Beitrag keine großen Geheimnisse offengelegt werden - es ist wahrscheinlicher für Anfänger in der Sprache, und ich habe beschlossen, es nur zu veröffentlichen, weil ich selbst auf ein Problem gestoßen bin und, nachdem ich keine fertige Lösung gefunden habe, selbst eine Art Multithreading-Emulation erstellt habe.

Die Aufgabe besteht also darin, eine große Datenmenge zu verarbeiten, die in unser Skript eingegangen ist. Meine Aufgabe war es, ein JSON-Array von Textinformationen zu verarbeiten und zu verdauen, aus denen das Skript ein ebenso großes Commit für PostgreSQL sammeln musste.

Zunächst sammeln wir die Daten in der übergeordneten Datei:

index.php

// bigdata.json -    .      - ,     .. $big_json = file_get_contents('bigdata.json'); $items = json_decode($big_json, true); //   php    ,    , ,  unset($big_json); // ... 

Die Array-Größe schwankte um 400 MB (später wurde sie auf ~ 50 MB reduziert), und alle Informationen waren in Textform. Es ist nicht schwer abzuschätzen, mit welcher Geschwindigkeit alles verdaut wurde, und da das Skript alle 15 Minuten auf cron ausgeführt wurde und die Verarbeitungsleistung mittelmäßig war, litt die Leistung sehr darunter.

Nach Erhalt der Daten können Sie deren Volumen schätzen und gegebenenfalls die erforderliche Anzahl von Threads für jeden CPU-Kern berechnen, oder Sie können einfach entscheiden, dass 4 Threads vorhanden sind, und die Anzahl der Zeilen für jeden Thread berechnen:

index.php

  // ... $threads = 4; $strs_per_thread = ceil(count($items) / $threads); //      -   echo "Items: ".count($items)."\n"; echo "Items per thread: ".$strs_per_thread."\n"; echo "Threads: ".$threads."\n"; // ... 

Es ist sofort erwähnenswert - eine solche "frontale" Berechnung liefert kein genaues Ergebnis anhand der Anzahl der Elemente für jeden Stream. Es ist wahrscheinlicher, die Berechnungen zu vereinfachen.

Und jetzt das Wesentliche: Wir erstellen Aufgaben für jeden Thread und führen ihn aus. Wir werden diese "Stirn" machen - eine Aufgabe für die zweite Datei erstellen - thread.php. Es fungiert als "Stream", der eine Reihe von Elementen zur Verarbeitung empfängt und unabhängig vom Hauptskript startet:

index.php

  // ... for($i = 0; $i < $threads; $i++){ if($i == 0) { passthru("(php -f thread.php 0 ".$strs_per_thread." & ) >> /dev/null 2>&1"); } if($i == $threads-1) { passthru("(php -f thread.php ".($strs_per_thread * $i)." ".count($items)." & ) >> /dev/null 2>&1"); } if(($i !== 0)&&($i !== $threads-1)) { $start = $strs_per_thread * $i + 1; $end = $start -1 + $strs_per_thread; passthru("(php -f thread.php ".$start." ".$end." & ) >> /dev/null 2>&1"); } } // ... 

Die Funktion passthru () wird verwendet, um Konsolenbefehle auszuführen, aber das Skript wartet, bis jeder von ihnen abgeschlossen ist. Zu diesem Zweck verpacken wir den Startbefehl in eine Reihe von Anweisungen, die den Prozess starten und sofort nichts zurückgeben. Wenn Sie den Prozess starten und der übergeordnete Prozess nicht auf die Ausführung jedes untergeordneten Elements wartet:

 #  ,    ,   Linux- (php -f thread.php start stop & ) >> /dev/null 2>&1 

Was genau hier vor sich geht, kann ich leider nicht mit Sicherheit sagen - eine Reihe von Parametern wurde mir von meinem vertrauten Linuxoid vorgeschlagen. Wenn Sie diese Magie in den Kommentaren entziffern können, werde ich dankbar sein und den Beitrag ergänzen.

Datei thread.php:

 $start = $argv[1]; $stop = $argv[2]; for ($i = $start; $i <= $stop; $i++) { // -          } 

Auf diese ganz einfache Weise können Sie die Multithreading-Emulation in PHP implementieren.

Wenn wir das gesamte Beispiel auf eine trockene Ausgabe reduzieren, würde dies meiner Meinung nach folgendermaßen klingen: Der übergeordnete Thread über die Befehlszeile startet untergeordnete Prozesse und teilt ihnen mit, welche Informationen verarbeitet werden sollen.

Wenn ich "Emulation" sage, meine ich, dass es mit dieser Implementierungsmethode keine Möglichkeit gibt, Informationen zwischen Threads oder zwischen übergeordneten und untergeordneten Threads auszutauschen. Es ist geeignet, wenn im Voraus bekannt ist, dass solche Funktionen nicht benötigt werden.

Source: https://habr.com/ru/post/de448464/


All Articles