Come posso propagare e rilevare errori generati in un altro thread in Raku?


9

Qual è il modo migliore per propagare gli errori da un thread separato (es. Start block, Proc :: Async o sub che li contengono). Il semplice wrapping del codice che gira via un nuovo thread in un blocco try / CATCH non funziona e l'utilizzo di wait funziona solo in base al valore di ritorno della sub routine (ovvero, un sé che ritorna secondario non funzionerà con l'approccio waitit).


Forse fooe barpuò essere eliminato qui?
jjmerelo,

1
Ho ancora problemi con questo scenario ... non è semplicemente possibile a Raku e richiede la ristrutturazione delle classi attuali? Non sarebbe l'ideale perché non voglio la gestione degli errori specifici dell'applicazione in classi che possono essere riutilizzate altrove ...
ryn1x

@ ryn1x Ti suggerisco di prendere in considerazione il ripristino di questa domanda nella sua forma originale. Quindi aggiungi una nota all'inizio spiegando che, sebbene alcune delle nostre risposte abbiano risolto l'affermazione del problema fornita nel corpo della tua domanda, in realtà stavi cercando qualcosa di più generale. Inoltre, sebbene la risposta che hai accettato fosse più generale, da allora hai concluso che non era ancora sufficientemente generale. Inoltre, hai provato una taglia, insieme a chiedere più generalità, ma non è stato d'aiuto. Quindi scrivi una nuova domanda, ricollegandola a questa, con un esempio che ritieni possa illustrare il problema.
raiph,

La risposta attuale è completamente sufficiente per me. Ho cambiato la domanda perché stava diventando troppo lunga e specifica per chi finisce qui.
ryn1x,

Risposte:


6

Usa await.

Ad esempio, sostituisci queste tre righe nel tuo codice:

foo;
bar;
baz;

con:

await foo, bar, baz;

Questo funziona, ma non si è adattato al mio vero problema perché foo, bar e baz sono in realtà metodi che restituiscono sé. Ho aggiornato la domanda e l'esempio.
ryn1x

5

Teoricamente, quel codice dovrebbe morire :

A partire dalla versione 6.d della lingua, il prefisso dell'istruzione start utilizzato nel contesto del sink collegherà automaticamente un gestore di eccezioni. Se si verifica un'eccezione nel codice specificato, verrà stampata e il programma verrà quindi chiuso, come se fosse stato lanciato senza alcun prefisso dell'istruzione start coinvolto.

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 questo caso è una situazione strana perché non stai sprofondando la promessa (la stai restituendo), ma alla fine la affondi perché la stai eseguendo in un contesto vuoto.

La stessa documentazione ti offre la soluzione: non affondare il contesto:

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

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

Dal momento che il tuo programma non muore, direi che sei nella seconda situazione. Per qualche motivo, non è affondato. Ma qualunque sia la situazione, la soluzione è la stessa: è necessario intercettare l'eccezione all'interno dello stesso blocco di codice.

Soluzione: awaitla promessa (che non la affonderà) o assegnarla a qualche variabile, in modo che anche il codice circostante muoia. Ma rispondendo al tuo OP, no, non puoi prendere un'eccezione da un altro thread, allo stesso modo non puoi prendere un'eccezione da un altro blocco.


Grazie per tutto questo In realtà devo essere più specifico rispetto al mio PO. Non sto chiamando nel contesto di sink e anche la soluzione wait non funziona perché le funzioni dell'OP sono in realtà metodi che restituiscono sé. Ho aggiornato la domanda e l'esempio.
ryn1x

4

Seguendo la convenzione usata in Go per passare gli errori fuori dalle routine usando i canali, ho trovato lo stesso approccio per lavorare in Raku. Si può usare un canale per inviare errori dal codice asincrono che devono essere gestiti dal thread principale.

Esempio:

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.