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

能够更严格控制这一点将非常有用,希望不仅仅是通过简单的setter,比如我们在core中为agents拥有的(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实现。

我冒昧地将与ioc/go在同一命名空间中的thread执行器移至该命名空间内,并用相同接口替换了原始的j.u.c Executor实例。此命名空间中声明的两个实例现在都采用了延迟(在延迟块中)的方式,以防止无用的线程池初始化/泄露。

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

我将在不久后把.patch文件附加到票证上。

0
by

评论者:mpenet

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

我们可以在创建channel时传递一个executor,这样可以相对容易地对该channel执行操作(部分示例在此 https://github.com/mpenet/core.async/commit/1449b3842052033cdf917ae92259ad9789722fdb)。

我相信这也是manifold的处理方式,但可能还有更多我尚未了解的内容。

0
by

评论者:mpenet

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

0
by

评论者:mpenet

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

0
by

评论由:gshayban

嗨,Max。我认为对此有一定的控制是必要的。

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

目前channel回调不知道它们唤醒了什么。我认为这是一个好的设计。如果它们唤醒了一个线程,这仅仅是通过传递一个promise来间接实现的(链接:1)
我认为channels.clj中不应该提及任何具体的executor。在多个不同地方运行go块可能是反模式,因为go块不应该阻塞或进行I/O。

但我非常欢迎对thread块的executor有更多的控制。

(链接: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之外的所有内容使用的线程池中运行,它可以**可选地通过一个参数提供的线程池来执行。默认设置未改变,如果不传递执行器,您将获得没有任何更改的核心.async当前行为。

一些人提到需要这个功能,有的公开,有的私下,包括我自己,我还知道有一个团队表达过同样的担忧,他们把他们的一个系统从core.async换成了manifold,因为后者提供了更多的此类控制。

最后这与控制有关,如果不在乎这个,它对core.async及其当前执行模型没有影响,但对于需要这种精细调优的人来说很重要。

几天前在twitter上又提到了这件事情: https://twitter.com/puredanger/status/576378306062262272,也可以在irc日志中找到相关的引用。

如果可能,我会将其作为一个库发布,但涉及的代码有时非常深入,几乎不可能在不修改整个库的情况下实现,因此才有了这个补丁。

0

评论者:mpenet

与上一个补丁相同,为promise-chan添加了缺少的arity

0

评论人:stu

我将我的投票视为对问题的肯定回答,而不一定是同意这里采取的方法。

在我看来,go和thread是不同的案例,值得分别考虑。我不清楚为什么chan需要被考虑,我也不想阅读一个补丁(或了解impl命名空间)来发现原因。能否有人在不提及实现的情况下解释不需要chan的原因?

对此话题的%H开发者列表有讨论吗?

0
2015年7月17日

评论者:mpenet

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

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

当这个补丁提交时,我在IRC上与Timothy Baldridge讨论了它
并按照他的建议做出了一些修改。既然你
可能和他共用办公室,我建议你和他讨论一下当前的
方法,因为他比我们两个都要熟悉代码库。

0
2018年5月8日

评论者:lxsameer

大家好,

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

目前只是一个草案,我想知道其他人对它的看法,然后再花更多时间在上面。请查看
我存放在 https://github.com/Codamic/core.async/commit/5cfa3ac34f963dc67329722149d041629a6c6e6f 的提交。

这应该能给你灵感。期待你的意见。

再见

0
2018年11月27日

评论者:mpenet

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

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

  • 添加arity到tread-call以允许传递执行器的补丁会被考虑吗?

  • 一个新的thread-call arity的宏版本可以是什么形式(如果它是有需求的;我认为一个新宏更有意义,因为thread接受一个&body,可能会在那里出现一些困难)。

  • 对各种threadpool初始化“延迟”的补丁会被考虑吗?

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