如何在 Raku 的另一个线程中传播和捕获抛出的错误?

How can I propagate and catch errors thrown in another thread in Raku?

从单独的线程(例如开始块、Proc::Async 或包含这些的子块)传播错误的最佳方法是什么。简单地将在 try/CATCH 块中分离出新线程的代码包装起来是行不通的,而使用 await 仅取决于子例程的 return 值(即 sub return ing self 不适用于 await 方法。

理论上,该代码 should die:

As of the 6.d version of the language, start statement prefix used in sink context will automatically attach an exceptions handler. If an exception occurs in the given code, it will be printed and the program will then exit, like if it were thrown without any start statement prefixes involved.

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 

在这种情况下,这是一个奇怪的情况,因为你没有下沉承诺(你正在归还它),但最终你下沉了它,因为你 运行 它处于无效的上下文中。

同一文档为您提供了解决方案:不要下沉上下文:

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

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

既然你的程序没有死掉,那我就说你属于第二种情况。由于某种原因,它没有沉没。但不管是什么情况,解决办法都是一样的:需要在同一个代码块内捕获异常。

解决方案:await 承诺(不会下沉)或将其分配给某个变量,这样周围的代码也会消失。但是响应你的 OP,不,你不能从另一个线程捕获异常,就像你不能从另一个块捕获异常一样。

使用await.

例如,在您的代码中替换这三行:

foo;
bar;
baz;

与:

await foo, bar, baz;

遵循 Go 中使用通道将错误传递出 go 例程的约定,我发现在 Raku 中也可以使用相同的方法。可以使用 Channel 从异步代码中发送错误,由主线程处理。

示例:

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;