【问题标题】:How to kill a thread, stop a promise execution in Raku如何杀死一个线程,停止 Raku 中的承诺执行
【发布时间】:2020-03-13 18:13:34
【问题描述】:

我正在等待停止(发送异常)到 SIGINT 上正在运行的承诺。 doc 中给出的示例退出整个过程,而不仅仅是一个工人。

有人知道如何“杀死”、“取消计划”、“停止”正在运行的线程吗?

这是针对p6-jupyter-kernel 问题或REPL issue

当前的解决方案是重新启动 repl 但不杀死阻塞的线程

await Promise.anyof(
  start {
      ENTER $running = True;
      LEAVE $running = False;
      CATCH {
          say $_;
          reset;
      }
      $output :=
        self.repl-eval($code,:outer_ctx($!save_ctx),|%adverbs);
  },
  $ctrl-c
);

【问题讨论】:

  • 在 Liz 的回答中,您会写“like pthread_cancel”。链接的文档开始“向线程发送取消请求”并描述了合作取消。 Raku 已经有合作的Cancellation。这就是 Brad 在 his answer to Is it possible to terminate a promise's code block from another promise? 中显示的内容,大概是 Liz 所说的“线程可以每隔一段时间检查一次标志,如果设置了该标志就决定将其退出。”
  • 我不知道以下内容是否与您的情况有关,但是...我找到了 Jonathan 几周前发布的 an answer related to cancellation。在其中他写道“看看使用Supply 范式,其中有一个取消模型(关闭水龙头)”。他在答案下方添加的评论中写道:“当我们在核心中有某种Promise 取消条款时”。需要明确的是,这是相对于非常不同的背景编写的,但在我看来,他提供的一些信息很可能与您的场景相关。
  • Tk @raiph,我没有看到那些帖子。我对它们进行了测试,它们阻止代码被执行(Cancelation)或停止等待(关闭tap),但不会停止一些后台执行。我会想办法不时询问一个线程来检查bool-must-die
  • 好吧,我忘了精确(重要)线程中运行的代码不在我手中。它属于 repl 的用户。但是,你是对的,检查.cancelled 是正确的方法。我仍然必须不时绕道“客户端”线程来检查它。
  • “在投入大量精力之前与乔纳森讨论这个问题” -> 我给他发了一封电子邮件。他绝对是给我出路的最佳人选。我想在低级别做一些工作是唯一的方法。必须附带一些高级别的测试。感谢您的提示。

标签: asynchronous raku


【解决方案1】:

短版:不要为此使用线程,使用进程。一般来说,杀死正在运行的进程可能是在这种情况下可以实现的最好的事情。

长答案:首先,有助于澄清问题中的一些困惑。

首先,没有“正在运行的Promise”之类的东西; Promise 是一种用于传达异步操作结果的数据结构。 start 块实际上在做三件事:

  1. 创建一个Promise(它的计算结果)
  2. 安排一些代码运行
  3. 安排运行该代码的结果通过保留或破坏Promise 来反映

这听起来可能有点学术,但确实很重要:Promise 不知道最终会保留或破坏它。

其次,start 块不是 - 至少对于内置调度程序 - 由线程支持,而是在线程池上运行。即使您可以找到一种“取出”线程的方法,线程池调度程序也不会对它希望从工作队列中吃掉的线程之一消失感到满意。您可以编写自己的调度程序,该调度程序确实每次都使用新线程返回工作,但这仍然不是一个完整的解决方案:如果用户请求执行自己的调度工作的代码片段,然后@987654329 @s 那个?然后没有一个线程可以杀死以真正使事情停止。

但是,让我们假设我们确实设法解决了所有这些问题,并且我们得到了一个列表,其中列出了一个或多个我们真的想在没有他们合作的情况下杀死的线程(合作情况相当容易;我们使用 @987654330 @ 并定期进行代码轮询,die 如果取消 Promise 被保留/损坏)。

任何此类机制如果希望能够停止在任何事物(不仅仅是计算,还有 I/O、锁定等)上阻塞的线程,都需要来自底层运行时(例如 MoarVM)的深度集成和协作。例如,尝试取消当前正在执行垃圾收集的线程将是一场灾难(很可能会使整个 VM 死锁)。其他不幸的取消时间可能会导致内存损坏,如果它正在执行一个不能安全中断的操作,如果被杀死的线程持有锁,则会导致其他地方的死锁,等等。因此,需要某种安全指向机制。 (我们在 MoarVM 中已经有了类似的东西来知道什么时候对 GC 是安全的,但是取消意味着不同的需求。它可能会横切 VM 代码库的许多部分。)

这还不是全部:同样的情况也在 Raku 语言级别上重复出现。例如,Lock::Async 不是底层运行时知道的一种锁。可能最好的办法是尝试拆除调用堆栈并运行所有LEAVE 移相器;这样就有希望了(如果人们使用.protect 方法;如果他们只是明确地调用lockunlock,我们就完了)。但即使我们设法不泄漏资源(这已经是一个很大的问题了),我们仍然不知道——一般来说——我们杀死的代码是否以任何一种一致的状态离开了世界。在 REPL 上下文中,这可能会导致访问相同全局状态的后续执行产生可疑的结果。这可能很烦人,但真正让我害怕的是人们在生产系统中使用这种取消机制——如果我们实现它,他们就会这样做。

因此,有效地实现这样的功能需要在运行时和 Rakudo 本身上做大量困难的工作,结果将是一个巨大的枪(我什至没有列举所有可能出错的事情,只是想到的前几个)。相比之下,杀死一个进程会清空所有资源,并且一个进程有自己的内存空间,所以也不存在一致性问题。

【讨论】:

  • 嗯,我认为这肯定回答了这个问题。我明白为什么没有霰弹枪可以杀死任何东西。我也讨厌危险的武器。所以没有通用的方法来解决这个线程停止(没有高级语言这样做)。所以(我坚持),让我争论一个停止阻塞REPL循环的具体方法。由于SIGINT 已经非常危险了(如果没有被捕获则杀死所有),它可以被异步捕获,请参阅其他评论。
  • 在 python 中,它们在主线程 KeyboardInterrupt 中捕获 SIGINT,并可以将其重新发送到 try catch。这与 perl 中的 signal.tap 声明非常接近,只是它与 Modules/_threadmodule.c 中的 lock acquire 同步触发。所以这导致了我的其他评论。
  • 也许我可以解决我的问题,而无需雇用您的低级技能来改变黑社会。我问how to kill a thread(坏人)。但是如果我问how to detour a thread (to death)(更好),你可能会有不同的想法。获取锁时是否可以使用锁同步运行某些代码? (我不是异步专家)-> 我知道你可以用锁来锁定代码。但是,即使使用其他类型的锁运行其他代码,您也能解锁代码吗?然后从other thread 订购detour your flow。谢谢@jonathan-worthington(我喜欢你的幻灯片,[今天]不要再骚扰你了 :-)
【解决方案2】:

如果不想停止线程,目前没有办法停止它。

线程可以每隔一段时间检查一次标志,如果设置了该标志,则决定将其退出。如果我们有办法从另一个线程在一个线程内抛出异常,那就太好了。但我们没有,至少据我所知没有。

【讨论】:

  • 嗨,丽兹,感谢您的快速回复。它会像pthread_cancel 一样有效。我会看看,但这是低级的东西。
  • 如果你研究这个,你必须通过 libuv 来做,因为那是 MoarVM 使用的抽象。或将其作为建议留在grants.perlfoundation.org
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-19
  • 1970-01-01
  • 2019-09-08
  • 1970-01-01
  • 2017-05-06
  • 2018-03-18
  • 2014-07-28
相关资源
最近更新 更多