请在2024年Clojure状态的调查问卷中分享您的想法!

欢迎!请参阅关于页面了解有关该功能的一些更多信息。

0
core.async

目前,core.async使用的线程池定义为全局变量,它们在各自的线程池中被所有go或线程宏共享

查看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中对代理采用的简单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 相同的命名空间,并使其支持相同的接口,而不是使用 raw j.u.c Executor 实例。该命名空间中声明的两个实例现在都是延迟的(在延迟块中),防止了无用的线程池初始化/泄露。

最后,我也修改了 thread-call 以支持相同的选项映射。

我将在稍后附上 .patch 文件到工单中。

0

评论者:mpenet

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

我们可以在通道创建时传递一个执行器,以相对容易地强制在此通道上执行操作(部分示例在此处 https://github.com/mpenet/core.async/commit/1449b3842052033cdf917ae92259ad9789722fdb)。

我相信 manifold 也是这样处理的,但我现在可能还有更多未知因素。

0

评论者:mpenet

带有 chan 修改的补丁。chan 现在可以接受一个额外的参数,该参数将是一个与 clojure.core.async.impl.protocols/Executor 兼容的执行器,如果提供,该执行器将被用于全局默认线程池。

0

评论者:mpenet

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

0

评论者:gshayban

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

主要有两个需求
1) 回调需要唤醒 parked go-blocks。
2) 运行更重的 thread(可能会阻塞,因此不能与同一个池共享)

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

我非常欢迎对thread块的executors进行一些控制。

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

0

评论者:mpenet

你好,Ghadi,

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

我没有改变你提到的(promise)的情况,改变的唯一一件事情是,对于clojure.core.async.impl.dispatch/run被调用的少数情况,这意味着函数在全局定义的线程池中运行,这个线程池被core.async中的所有内容使用,除了thread,它可以***可选地通过提供的线程池函数参数完成。默认值没有改变,如果你没有传递executors,你将保持核心async当前的行为,没有任何改变。

一些人都提到了这样的需求,有些在公共场合,有些在私下,包括我自己,我还知道有一个团队表达了相同的问题,并刚刚将其系统从core.async切换到manifold,因为后者提供了更多类似的控制。

最终,这关乎控制,如果你不在乎它,它对core.async及其当前执行模型没有造成任何变化,但对于需要这种精细调优的人来说,它很重要。

几天前推特上又提到了这个问题: https://twitter.com/puredanger/status/576378306062262272,并且你肯定也可以在irc日志中找到相关的引用。

如果可能的话,我会将其作为一个库发布,但由于相关的代码有时非常深入地植根于其中,不进行整个库的分支就几乎不可能完成,因此产生了这个补丁。

0

评论者:mpenet

与先前补丁相同,添加了promise-chan缺失的arity

0

评论者:stu

把我的投票看作是对问题的赞成,而不一定是对这里采取的方法的赞成。

在我看来,go和thread是两个不同的情况,值得分别考虑。我不明白为什么需要对chan进行考虑,我不想在读补丁(或学习有关impl命名空间)时发现为什么。有人能解释一下不提及实现的情况下chan的情况吗?

对此有开发列表上的讨论吗?

0

评论者:mpenet

是的,只要这个问题被改进或修复。

如果你阅读了补丁或者channels的源代码,你会看到
channels在其线程池上运行回调。

当这个补丁提交时,我花费时间与Timothy Baldridge在IRC上讨论这个问题,并按照他的建议做了一些修改。
由于你可能与他在同一个办公室工作,我建议你和他讨论当前的方法,因为他比我俩都对代码库更熟悉。
可能的话。
他会比我俩都熟悉代码库。

0

评论由:lxsameer

嘿,伙计们,

我在threadpool ns中添加了几个函数,以便用户可以为主线程池和`thread`宏提供一个执行器。

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

它应该会给你一些想法。期待你的意见。

祝好

0

评论者:mpenet

重新阅读我多年前提出这个问题的细节后,现在我实际上同意Stu和Ghadi的评论,我认为我对core.async go内部结构的理解现在好多了,在这个层面上线程(以及回调)只是为了“监控”/上下文切换,因此对这些的精细控制并不那么重要。

我认为一个更好的线程调函数以及可能另一个类似thread的宏(接受用户提供的执行器)还有很多适用的案例。

  • 给`thread-call`添加一个参数,以便传入执行器,这个补丁会被考虑吗?

  • 如果需要的话,新的线程调用参数的宏版本会是什么形式?(我认为一个新的宏比thread接收一个&body更有意义,因为这可能有点难以适应那里)。

  • 对各种线程池初始化进行“懒加载”(延迟)的补丁会被考虑吗?

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