请在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中对代理(set-agent-send-executor!)那样的简单设置器来完成,因为它一次只允许使用一个线程池。
一个可能的解决方案是通过thread-call的选项映射参数来实现,以及/或go块的其他机制(具有相同签名签名的go-call)。将此参数当作映射也会允许在未来必要时进一步扩展内部结构,而无需对API进行重大更改。

12 答案

0 投票

评论者:mpenet

我在这里有一个补丁,https://github.com/mpenet/core.async/commit/1c2b0a9af9a5e891af0f1631b8debd337a73999d,它添加了这个功能。

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

我自行将thread执行器移动到了与ioc/go相同的命名空间中,并使其支持相同的接口,而不是使用原始的java.util.concurrent.Executor实例。现在在这个命名空间中声明的两个实例都是延迟加载的(在一个延迟块中),这可以防止无用的线程池初始化/泄漏。

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

我将很快将.patch文件附加到事务中。

0 投票

评论者:mpenet

实际上这个补丁是不完整的,我正在发现ioc宏重写会导致对全局线程池的调用(特别是通过put(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)运行 heavier thread (可能阻塞,因此不能与同一池共享)

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

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

(链接: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之外的所有内容,现在可以通过一个提供线程池的函数参数进行选择性地完成。默认值未更改,如果您不传递执行器,则得到当前的master core.async行为,没有任何变化。

少有人提到这种需求,一些在公开场合提到,一些在私下提到,包括我自己,我还知道一个团队因为后者提供了更多类似的控制选项,所以他们将从core.async切换到manifold。

最终,这是关于控制的问题,如果您对此不感兴趣,这并不改变core.async及其当前执行模型,但对于需要这种精细调整的人来说,这是重要的。

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

如果可能的话,我会将其作为一个库发布,但涉及到的代码有时非常深植,如果不进行整个库的分支,几乎无法做到这一点,因此产生了这个补丁。

0 投票

评论者:mpenet

与之前的补丁相同,增加了missing arity到promise-chan

0 投票

评论者:stu

将我的点赞看作是对问题的赞赏,不一定是对这里采取的方法的赞赏。

在我看来,go和thread是不同的案例,值得分别考虑。我认为channel需要考虑,但不清楚chan需要考虑的原因,我不想阅读补丁或了解实现命名空间来了解这一点。能有人解释一下chan的情况,不参考实现吗?

关于此的开发列表讨论了吗?

0 投票

评论者:mpenet

当然,只要这个问题得到改善或解决。

如果您会阅读补丁或channel的源代码,您会看到
channel在其回调线程池中运行。

当这个补丁被提交时,我和Timothy在IRC上进行了讨论,并接受了他的建议进行了一些修改。既然你
可能和他共用办公室,我建议你和他讨论当前的方法,因为他比我们两个人都对代码库更熟悉。


0 投票

评论者:lxsameer

大家好,

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

这只是一个草案,我想知道其他人对它的看法,然后再花更多时间在这个问题上。请查看
我在以下位置的提交

https://github.com/Codamic/core.async/commit/5cfa3ac34f963dc67329722149d041629a6c6e6f

它应该会让你有一个大概的了解。期待您的意见。

加油

0 投票

评论者:mpenet

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

我认为仍有理由创建一个更好的thread-call函数,并且可能还需要另一个类似于thread的宏,它接受用户提供的执行器。

  • 会给tread-call添加一个参数来传递执行器的补丁会被考虑吗?

  • 那个新的thread-call参数的宏版本可以是什么形式(如果它是有用的;我认为新的宏更有意义,因为thread需要接受一个可能会有些棘手的&body参数)。

  • 会考虑一个使各种threadpool初始化“延迟”的补丁吗?

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