Start · Sprachen · PHP · Referenz · Swoole\Coroutine\Lock

Swoole\Coroutine\Lock

Klasse

Bietet eine nicht blockierende, coroutinen-freundliche Sperre (<code>Mutex</code>), die prozess- und thread-übergreifend geteilt werden kann.

seit PHP 6.0.0 Kategorie: misc

Signatur

class Swoole\Coroutine\Lock

Beschreibung

Swoole\Coroutine\Lock implementiert einen Mutual-Exclusion-Lock (Mutex) speziell für den Einsatz in Swoole-Coroutinen. Im Gegensatz zu einem klassischen blockierenden Mutex suspendiert diese Sperre beim Warten lediglich die aktuelle Coroutine und gibt die CPU an andere Coroutinen frei — der Worker-Prozess blockiert also nicht.

Die Klasse ist besonders nützlich, wenn mehrere Coroutinen gleichzeitig auf eine gemeinsame Ressource (z. B. eine Datei, eine Datenbankverbindung oder eine geteilte Variable) zugreifen und dabei Race Conditions vermieden werden müssen. Da der Lock über Prozess- und Thread-Grenzen hinweg geteilt werden kann, eignet er sich auch für Multi-Process- oder Multi-Thread-Szenarien, die in Swoole mit Swoole\Process oder Swoole\Thread realisiert werden.

Mit lock() wird die Sperre gesetzt (bei Konkurrenz wird die Coroutine suspendiert), mit trylock() kann eine Sperre nicht-blockierend versucht werden, und mit unlock() wird die Sperre wieder freigegeben. Es ist darauf zu achten, dass immer dieselbe Coroutine, die lock() aufgerufen hat, auch unlock() ausführt, um Deadlocks zu vermeiden.

Der Lock muss vor dem Erstellen von Kind-Prozessen oder -Threads instanziiert werden, damit er im gemeinsamen Speicher liegt und wirklich geteilt werden kann. Eine Nutzung rein innerhalb einer einzelnen Coroutine ohne Konkurrenz ist zwar möglich, aber unnötig.

Beispiele

Coroutinen-sicherer Zähler mit Lock

<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Lock;
use function Swoole\Coroutine\run;

run(function () {
    $lock    = new Lock();
    $counter = 0;

    $worker = function () use ($lock, &$counter) {
        for ($i = 0; $i < 100; $i++) {
            $lock->lock();       // Sperre setzen — suspendiert die Coroutine bei Konkurrenz
            $counter++;          // kritischer Abschnitt
            $lock->unlock();     // Sperre freigeben
        }
    };

    // Zwei Coroutinen laufen gleichzeitig
    $c1 = Coroutine::create($worker);
    $c2 = Coroutine::create($worker);

    // Auf beide Coroutinen warten (vereinfacht)
    Coroutine::sleep(0.1);

    echo "Endwert des Zählers: {$counter}\n"; // Erwartet: 200
});
Endwert des Zählers: 200

Nicht-blockierender Versuch mit trylock()

<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Lock;
use function Swoole\Coroutine\run;

run(function () {
    $lock = new Lock();

    Coroutine::create(function () use ($lock) {
        $lock->lock();
        echo "Coroutine 1: Sperre gesetzt\n";
        Coroutine::sleep(0.05); // Arbeit simulieren
        $lock->unlock();
        echo "Coroutine 1: Sperre freigegeben\n";
    });

    Coroutine::create(function () use ($lock) {
        Coroutine::sleep(0.01); // Sicherstellen, dass C1 zuerst sperrt
        if ($lock->trylock()) {
            echo "Coroutine 2: Sperre erhalten\n";
            $lock->unlock();
        } else {
            echo "Coroutine 2: Sperre war besetzt, überspringe\n";
        }
    });

    Coroutine::sleep(0.2);
});
Coroutine 1: Sperre gesetzt Coroutine 2: Sperre war besetzt, überspringe Coroutine 1: Sperre freigegeben

// Wichtig · Fallstricke

Deadlock-Gefahr: Ruft dieselbe Coroutine lock() zweimal auf, ohne zwischendurch unlock() aufzurufen, entsteht ein Deadlock. Swoole\Coroutine\Lock ist kein rekursiver (reentrant) Mutex.

Reihenfolge der Instanziierung: Der Lock muss vor dem Fork (z. B. Swoole\Server::start()) oder dem Starten von Threads erzeugt werden, damit er im shared Memory liegt und prozessübergreifend genutzt werden kann.

Nur in Coroutinen-Kontext: Außerhalb einer Coroutine-Umgebung (z. B. im normalen synchronen PHP-Skript ohne Swoole-Event-Loop) verhält sich lock() blockierend und kann den gesamten Prozess anhalten.

Verfügbarkeit: Diese Klasse steht erst ab Swoole 6.0 zur Verfügung und erfordert eine PHP-Version ≥ 8.1 sowie einen mit Coroutine-Unterstützung kompilierten Swoole-Build.