Alternative zu get_posts () aufgrund eines Multithreading-Cache-Absturzes

8

Ich verwende pthreads, um mehrere Threads zu erstellen. Jeder dieser Threads versucht an einem Punkt get_posts()Folgendes zu verwenden:

$args = array(
    'post_type' => 'post',
    'post_status' => 'any'
);

$posts_list = get_posts($args);

Am Ende habe ich jedoch folgenden Absturz:

HP Fatal error:  Call to a member function get() on a non-object in C:\dev\wordpress\wp-includes\cache.php on line 123

BITTE BEACHTENget_posts() SIE, dass ich keinen Absturz habe, wenn ich denselben Aufruf in einem Codeabschnitt ohne Thread mache.

Nun meine Frage, wie man get_posts()aus einem Pthread- Thread heraus aufruft ? Und wenn ich das nicht kann, was ist die Alternative?

Vielen Dank.


Aktualisieren

Hier ist ein Beispielcode

class My_Thread extends Thread {

    public function run() {

        /* DO SOME STUFF HERE */

        $args = array(
            'post_type' => 'post',
            'post_status' => 'any'
        );

        $posts_list = get_posts($args); // <------ This is causing the crash
    }
}

// Create a array
$threads = array();

//Iniciate Miltiple Thread
foreach ( range("A", "C") as $i ) {
    $threads[] = new My_Thread($i);
}

// Start The Threads
foreach ($threads as $thread) {
    $thread->start();
}
Greeso
quelle
Das ist kein Absturz, es ist ein Fehler. Sie sollten Ihren Code korrigieren, damit kein Fehler auftritt. In jedem Fall sind PHP-Bibliotheken nicht immer Multitasking-sicher, sodass das Problem möglicherweise bei etwas völlig anderem liegt.
Mark Kaplun
Wenn es Code gibt, der für die Ausführung "zur gleichen Zeit" geschützt werden muss, müssen Sie Mutexe verwenden, aber das liegt hier weit außerhalb des Anwendungsbereichs.
Mark Kaplun
@ MarkKaplun - Danke für deine Eingabe. Es scheint jedoch, als hätten Sie den Punkt verpasst, an dem ich feststelle, dass " wenn ich denselben get_posts()Aufruf in einem Codeabschnitt tätige, der kein Thread ist, habe ich keinen Absturz "; Es ist also kein Problem mit meinem get_posts($args)Anruf. Außerdem gibt es an dieser Stelle keinen Code, der geschützt werden muss. Ich lese nur aus der WordPress-Datenbank über get_posts($args).
Greeso
3
@ MarkKaplun - Was ist los mit dir? Warum bist du so negativ und so aggressiv? Warum verstehe ich Multitasking nicht und schlage vor, keine Pthreads zu verwenden? Selbst wenn Sie richtig liegen, sollten wir nicht versuchen, was wir nicht verstehen, um unser Wissen und unsere Grenzen zu erweitern? Und geht es auf dieser Website nicht darum, Fragen zu stellen, wenn Sie nicht wissen, wie Sie eine bestimmte Sache tun? Ich tue nichts vor. Ich bin auf einen Fehler gestoßen, habe festgestellt, dass dies auf die Verwendung von pthreads zurückzuführen ist, und frage nach einer Lösung, entweder nach einem Setup oder nach einer Programmierumgehung. Ich hatte auf eine konstruktive Antwort von Ihnen gehofft.
Greeso
2
Bis wir wirklich wissen, dass WordPress nicht der Grund ist, diesen Code zu brechen, ist es ein Thema.
Fuxia

Antworten:

2

Da die Frage so viele positive Stimmen enthält, obwohl die Probleme des Multithreading für ein Antwortformat einfach zu weit gefasst sind, werde ich versuchen zu erklären, warum Sie die WordPress-API nicht auf Multithread-Weise verwenden sollten.

TL; DR - PHP wird nicht als Multithreading-fähig angenommen. Das Problem ist nicht PHP selbst, sondern hauptsächlich die verwendeten Bibliotheken. Aus diesem Grund wird empfohlen , den Multithread-Ausführungsmodus in Apache nicht zu verwenden, obwohl er theoretisch etwas schneller sein sollte. Um das Problem zu verstärken, dass die zugrunde liegende Ebene nicht Multithread-fähig ist, verstößt der WordPress-Kern gegen die grundlegendste Anforderung von Multithread - kein freier Zugriff auf Globals.

Was ist das Problem mit Globals in Multithread-Umgebungen? Nehmen wir an, wir haben den naiv aussehenden Code

function inc() {
  global $g;

  $g++;
}

Obwohl es sich nur um einen Einzeiler handelt, handelt es sich nicht um eine atomare Operation für die CPU, und es sind mehrere Anweisungen auf Maschinenebene erforderlich, um sie tatsächlich auszuführen. Etwas wie

move $g to register D
increment register D
move register D to $g

Nehmen wir nun an, wir haben zwei Threads AB, inc()die "zur gleichen Zeit" aufrufen (offensichtlich gibt es bei nur einer CPU nicht die gleiche Zeit) und dass der Anfangswert von $ g 0 ist, was der Wert von $ wäre g nachdem beide Threads fertig sind? Dies hängt davon ab, wie das Betriebssystem mit Multithreading umgeht und wann es zwischen Threads wechselt. In "älteren" Betriebssystemen war es die Aufgabe des Threads, durch Aufrufen einer API zu deklarieren, dass die Steuerung von ihr übernommen werden kann. Dies führt jedoch zu vielen Problemen mit Prozessen, die sich schlecht verhalten und das System dafür in "modernen" Betriebssystemen sperren, die das Betriebssystem übernimmt Kontrolle, wann immer es sich anfühlt. Im wirklichen Leben wird das Ergebnis des Codes sein, dass $ g einen Wert von 2 hat, aber es gibt auch die folgende Möglichkeit

Im Kontext von A.

move $g to register D
// value of D is 0
// OS stores the content of registers and switches to thread B
// B increments $g to 1 and finishes working
// OS restores content of registers to the context of thread A
// Value of register D is now 0
increment register D
move register D to $g

Das Endergebnis ist, dass $ g den Wert 1 hat.

Offensichtlich sind Globale nicht das einzige Problem, und die Behandlung von Ein- und Ausgängen ist auch ein Kern für Mutithreading-Probleme.

Im richtigen Multithreading-Code verwenden Sie lock / mutex / semaphore / pipe / socket ...., um den Zugriff auf solche globalen Ressourcen zu serialisieren und sicherzustellen, dass die Operation ein vorhersehbares Ergebnis liefert. Wordpress macht das nicht.

Zur Hölle, WordPress ist nicht einmal mehrprozesssicher. Meistens kommt es damit durch, weil das DB-Schema so aufgebaut ist, dass im realen Leben nicht die gleichen Daten aus verschiedenen Prozessen geändert werden müssen (verschiedene Posts haben unterschiedliche Zeilen und teilen keine Daten) den Code der Seitenleiste / der Widgets und versuchen Sie sich vorzustellen, was passieren wird, wenn zwei Administratoren versuchen würden, genau zur gleichen Zeit ein anderes Widget hinzuzufügen. Da dies die Manipulation einer bestimmten Option erfordert, können entweder beide Widgets oder nur eines davon hinzugefügt werden.

Zurück zum Multithrading. Unter Unix sind im Gegensatz zu Windows die zusätzlichen Kosten für das Laichen eines Prozesses anstelle eines Threads vernachlässigbar. Daher ist die Verwendung wp_remote_geteiner speziellen URL zum Aufrufen eines zusätzlichen "Threads" eine sehr legitime Maßnahme, um fast alle mit Multithreading verbundenen Fallstricke zu vermeiden.

Mark Kaplun
quelle
Dies ist gut erklärt. Vielen Dank. Ich habe auch gerade herausgefunden, dass die Unterstützung für Pthreads zur Arbeit mit Apache entfernt wird. Damit pthreads funktionieren, sollte es sich innerhalb einer CLI- Umgebung befinden. Für mich brauche ich Pthreads , aber ich werde diese Lösung bis nach der Veröffentlichung verschieben (dh eine Verbesserung). Außerdem muss ich WordPress als CLI-Umgebung einrichten (Details hier wp-cli.org ). Auf diese Weise kann ich eine pthreads / WordPress-Umgebung über die CLI bearbeiten, sodass ich die schwere Arbeit im Backend ohne Apache erledigen kann. Nochmals vielen Dank.
Greeso
Nur um hinzuzufügen, werde ich pthreads auf Probleme beschränken, die nicht mit der Datenbank zu tun haben. Verwenden Sie gemäß Ihrem Vorschlag Mutex für Datenbankschreibvorgänge.
Greeso
@Greeso, Linux wurde entwickelt, um mehrere Prozesse zu verwenden, um gleichzeitige Ausführungsanforderungen zu erfüllen. Das Laichen eines neuen Prozesses ist wirklich sicherer und so schnell wie das Verwenden von Pthreads.
Mark Kaplun