2024年Clojure状态调查!中分享您的想法。

欢迎!请参阅关于页面,了解有关如何使用本网站的一些更多信息。

0 投票
core.async

目前core.async使用的线程池被定义为全局变量,它们被各自的go或thread macro在其池中进行共享。

参见https://github.com/clojure/core.async/blob/ac0f1bfb40237a18dc0f03c0db5df41657cd23a6/src/main/clojure/clojure/core/async.clj#L391-L411
https://github.com/clojure/core.async/blob/ac0f1bfb40237a18dc0f03c0db5df41657cd23a6/src/main/clojure/clojure/core/async/impl/dispatch.clj#L16

非常需要对此有更多的控制,希望不是通过像我们在core中对agents(set-agent-send-executor!)中有的那种简单setter,因为那只能一次使用一个池。
一个可能的解决方案是通过thread-call的选项映射参数,以及/或对go块(具有相同签名的go-call)的其他机制。将此参数作为映射还有助于在必要时进一步扩展内部,而无需对API进行重大更改。

12 答案

0 投票

评论由:mpenet

我在这里有个补丁https://github.com/mpenet/core.async/commit/1c2b0a9af9a5e891af0f1631b8debd337a73999d,它增加了该特性。

它增加了2个宏:go*和thread**。这些与go/thread相同,但首先接收一个选项映射,然后是主体。选项映射可以接收一个带有clojure.core.async.impl.protocols/Executor实现的:executor键。

我趁机将 thread 执行器移动到与 ioc/go 相同的命名空间中,并支持相同的接口,而不是使用原始的 j.u.c Executor 实例。此命名空间中声明的两个实例现在都是懒惰的(在延迟块中),防止了无用的线程池初始化/泄露。

最后,我还修改了 thread-call 以支持同一个选项映射。

我将很快将 .patch 文件附加到票据中。

0 投票
by

评论由:mpenet

实际上,这个补丁是不完整的,我刚刚发现 ioc-macros 重写会导致调用全局线程池(特别是通过在 put(image: , take) 回调中进行调用)。

我们可以允许在创建通道时传递一个执行器,以便轻松地在该通道上执行操作(这里是一个部分示例 https://github.com/mpenet/core.async/commit/1449b3842052033cdf917ae92259ad9789722fdb)。

我相信 manifold 也是这样处理的,但我在这一点上可能还有更多未知的内容。

0 投票
by

评论由:mpenet

包含 chan 修改的补丁。现在 chan 可以接受一个额外的参数,该参数将是一个 clojure.core.async.impl.protocols/Executor 兼容的执行器,当提供时,将使用该执行器而不是全局默认池。

0 投票
by

评论由:mpenet

提供补丁作为单个提交(更易于阅读)。您还可以在此处查看更改:https://github.com/mpenet/core.async/commit/e7dea04553935863d1abb1880d84bbdc273854ec

0 投票
by

评论者:gshayban

嗨 Max。我认为更好地控制这一点是必要的。

基本上有两个需求
1) 回调需要唤醒已停泊的 go-block。
2) 运行更重的 thread(这些线程可能阻塞,因此不能与同一个池共享)

目前通道回调不知道它们唤醒了什么。我认为这是一个好的设计。如果它们唤醒一个线程,只是通过传递一个承诺(链接:1)间接地这样做
我认为在 channels.clj 中不应提及其他特定的执行器。在多个不同的位置运行 go-block 可能是一个反模式,因为 go-block 不应该发生阻塞或进行 I/O。

我非常欢迎对 thread 块的执行器有一定的控制。

(链接:1) https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async.clj#L137

0 投票
已回答

评论由:mpenet

Hi Ghadi,

感谢您的反馈,但我认为您没有正确阅读我的补丁。

我没有更改您提到的那个案例(promise),唯一更改的是,对于clojure.core.async.impl.dispatch/run被调用的那几个案例(意味着该函数在全局定义的threadpool中运行,而thread除外,这个threadpool被core.async中的所有功能使用),可以 可选地 通过提供参数化threadpool来完成。默认值保持不变,如果您不传递执行器,您将获得无需任何更改的核心.async的当前行为。

有人提到需要这个功能,一些在公共场合,一些在私下里,包括我自己,我知道有一支团队表达过类似的关切,并刚刚将他们的一个系统从core.async切换到了manifold,因为后者提供了更多这类功能。

最终,这关乎控制。如果您不关心这个问题,它对core.async及其当前执行模型没有任何影响,但对那些需要精细度调整的人来说,这是非常重要的。

几天前它又在twitter上被提及了:[链接](https://twitter.com/puredanger/status/576378306062262272) ,你肯定可以在irc日志中找到它的参考文献。

如果可能,我会将其作为lib发布,但涉及的代码有时候非常深入,如果不改整个库,几乎无法做到,因此才有这个补丁。

0 投票
已回答

评论由:mpenet

与前一个补丁一样,为promise-chan添加了缺失的参数。

0 投票
已回答

由:stu 发布的评论

把我的点赞视为对问题的赞同,不一定是对这种方法的赞同。

在我看来,go和thread是两个不同的案例,值得独立考虑。对我来说,chan为什么要考虑并不清楚,我不想通过阅读补丁(或了解impl命名空间)来发现原因。有人能解释不涉及实现的情况下chan的案例吗?

关于这个问题的开发者列表讨论了吗?

0 投票
已回答

评论由:mpenet

当然,只要这个问题得到了改善/修复。

如果您读过补丁,或者channel的源代码,您会看到
channel在其threadpool上运行回调。

当我提交这个补丁时,我和Timothy讨论过这个问题。
在IRC上与Baldridge进行了交流并对他的建议进行了一些修改。
鉴于你们可能与他共用办公室,我建议您与他讨论当前的方法,因为他比我们两个都更熟悉代码库。

0 投票

评论者:lxsameer

大家好,

我向threadpool ns添加了一些函数,以便用户为主线程池以及`thread`宏提供执行器。

目前这是个草稿,我想知道其他人对此的看法,在投入更多时间之前请看看我repo里的提交
https://github.com/Codamic/core.async/commit/5cfa3ac34f963dc67329722149d041629a6c6e6f

这应该能给你一些启发。期待您的意见。

感谢

0 投票

评论由:mpenet

在我重新阅读了我几年前创建的这个问题的细节后,我现在实际上是同意Stu和Ghadi的评论的,我认为我对core.async go内部的理解现在好多了,在这个级别上(以及回调)的线程仅用于“监督”/上下文切换,所以对这些方面进行更细粒度的控制并不那么重要。我认为我可能受到了manifold的设计的影响,其设计在这方面的差异很大(以及其他方面),但这两个库是相当不同的。

我认为仍然有必要有一个更好的thread-call函数,可能还需要另一个像thread这样的宏,它接受用户提供的执行器。

  • 能否考虑添加一个arity到`thread-call`,以允许传递执行器的一个补丁?

  • 如果需要的话,这个新的thread-call arity的宏版本可以采取什么形式(我认为一个新的宏在这里会更合理,因为thread接受一个可能有点难以改装的&body)。

  • 能否考虑添加使各种threadpool初始化“惰性”的补丁(即延迟)?

0 投票
参考: https://clojure.atlassian.net/browse/ASYNC-94(由mpenet报告)
...