Wie kann ich Fehler verbreiten und abfangen, die in einem anderen Thread in Raku ausgelöst wurden?

9

Was ist der beste Weg, um Fehler aus einem separaten Thread zu verbreiten (z. B. Startblock, Proc :: Async oder Sub, der diese enthält). Das einfache Umschließen des Codes, der einen neuen Thread in einen try / CATCH-Block spinnt, funktioniert nicht, und die Verwendung von await funktioniert nur in Abhängigkeit vom Rückgabewert der Subroutine (dh ein Sub, das self zurückgibt, funktioniert nicht mit dem await-Ansatz).

ryn1x
quelle
Vielleicht foound barkann hier beseitigt werden?
jjmerelo
1
Ich habe immer noch Probleme mit diesem Szenario ... ist es in Raku einfach nicht möglich und erfordert eine Umstrukturierung der tatsächlichen Klassen? Das wäre nicht ideal, da ich keine anwendungsspezifische Fehlerbehandlung in Klassen möchte, die an anderer Stelle wiederverwendet werden können ...
ryn1x
@ ryn1x Ich schlage vor, Sie erwägen, diese Frage in ihrer ursprünglichen Form wiederherzustellen. Fügen Sie dann zu Beginn eine Notiz hinzu, in der erläutert wird, dass Sie, obwohl einige unserer Antworten die im Text Ihrer Frage angegebene Problemstellung gelöst haben, tatsächlich nach etwas Allgemeinerem gesucht haben. Obwohl die Antwort, die Sie akzeptiert haben, allgemeiner war, haben Sie inzwischen festgestellt, dass sie immer noch nicht allgemein genug ist. Außerdem, dass Sie ein Kopfgeld versucht haben, gepaart mit der Bitte um mehr Allgemeinheit, aber das hat nicht geholfen. Schreiben Sie dann eine neue Frage, die auf diese zurückführt, mit einem Beispiel, von dem Sie glauben, dass es das Problem veranschaulicht.
Raiph
Die aktuelle Antwort reicht mir völlig aus. Ich habe die Frage geändert, weil sie für jeden, der hier landet, zu lang und spezifisch wurde.
ryn1x

Antworten:

6

Verwenden Sie await.

Ersetzen Sie beispielsweise diese drei Zeilen in Ihrem Code:

foo;
bar;
baz;

mit:

await foo, bar, baz;
Raiph
quelle
Dies funktioniert, ist aber nicht auf mein eigentliches Problem skaliert, da foo, bar und baz tatsächlich Methoden sind, die sich selbst zurückgeben. Ich habe die Frage und das Beispiel aktualisiert.
ryn1x
5

Theoretisch sollte dieser Code sterben :

Ab der 6.d-Version der Sprache wird durch das im Senkenkontext verwendete Präfix für Startanweisungen automatisch ein Ausnahmebehandlungsprogramm angehängt. Wenn im angegebenen Code eine Ausnahme auftritt, wird diese gedruckt und das Programm wird beendet, als ob es ohne Präfixe für Startanweisungen ausgelöst worden wäre.

use v6.c;
start { die }; sleep ⅓; say "hello"; # OUTPUT: «hello␤» 

use v6.d;
start { die }; sleep ⅓; say "hello";
# OUTPUT: 
# Unhandled exception in code scheduled on thread 4 
# Died 
#     in block  at -e line 1 

In diesem Fall ist es eine seltsame Situation, weil Sie das Versprechen nicht versenken (Sie geben es zurück), aber schließlich versenken Sie es, weil Sie es in einem leeren Kontext ausführen.

Dieselbe Dokumentation gibt Ihnen die Lösung: Versenken Sie den Kontext nicht:

# Don't sink it: 
my $ = start { die }; sleep ⅓; say "hello"; # OUTPUT: «hello␤» 

# Catch yourself: 
start { die; CATCH { default { say "caught" } } };
sleep ⅓;
say "hello";

Da Ihr Programm nicht stirbt, würde ich sagen, dass Sie sich in der zweiten Situation befinden. Aus irgendeinem Grund ist es nicht versenkt. In jeder Situation ist die Lösung dieselbe: Sie müssen die Ausnahme innerhalb desselben Codeblocks abfangen.

Lösung: awaitdas Versprechen (das es nicht versenkt) oder es einer Variablen zuweisen, so dass auch der umgebende Code stirbt. Wenn Sie jedoch auf Ihr OP antworten, können Sie keine Ausnahme von einem anderen Thread abfangen, genauso wie Sie keine Ausnahme von einem anderen Block abfangen können.

jjmerelo
quelle
Danke für all das. Ich muss tatsächlich spezifischer sein als in meinem OP. Ich rufe nicht im Senkenkontext auf und die Wartelösung funktioniert auch nicht, da die Funktionen aus dem OP tatsächlich Methoden sind, die sich selbst zurückgeben. Ich habe die Frage und das Beispiel aktualisiert.
ryn1x
4

Nach der in Go verwendeten Konvention zum Übergeben von Fehlern aus Go-Routinen mithilfe von Kanälen habe ich den gleichen Ansatz für die Arbeit in Raku gefunden. Man kann einen Kanal verwenden, um Fehler aus dem asynchronen Code zu senden, der vom Hauptthread behandelt werden soll.

Beispiel:

my $errors = Channel.new;

my $err-supply = $errors.Supply;
$err-supply.tap(-> $e {say "handle error: $e"});

start {
    die "something went horribly wrong";

    CATCH {
        default {
            $errors.send($_);
        }
    }
}

sleep 1;
ryn1x
quelle