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中为agents(set-agent-send-executor!)使用的设置器),因为它一次只允许使用一个线程池。
一个可能的解决方案是通过向thread-call传递一个选项映射参数,以及/或为Go块提供其他机制(使用签名相同的go-call)。将此参数作为映射也将允许在未来(如果需要)进一步扩展内部结构,而无需进行重大的API更改。

12 个答案

0投票
by

评论者:mpenet

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

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

我将thread executor移动到与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) 回调需要唤醒parked go-blocks。
2) 运行较重的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及其当前执行模式没有任何影响,但对那些需要这种细粒度调整的人来说非常重要。

几天前在推特上又提到了这个问题:[链接](https://twitter.com/puredanger/status/576378306062262272),你当然也可以在irc日志中找到相关引用。

如果可能的话,我会将其发布为一个库,但是涉及的代码有时深植其中,几乎不可能在不分支整个库的情况下完成,因此才有了这个补丁。

0投票

评论者:mpenet

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

0投票

stu做出的评论:

我对此问题,而非此处采取的方法的赞是不//---------------------------------------------------------------------------------------------------------------------

在我看来,go和thread是两个不同的案例,值得分别考虑。我不清楚为什么chan需要进行考虑,并且我不想通过阅读补丁(或了解impl命名空间)来发现原因。有没有人可以解释一下不含实现参考的chan的案例?

关于这一点,开发者列表上有讨论吗?

0投票

评论者:mpenet

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

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

当这个补丁被提交时,我在IRC上与Timothy Baldridge进行了讨论,并按照他的建议做了一些更改。
既然您可能与他共用办公室,我建议您与他讨论当前
的方法,因为他比我们两个都更熟悉代码库。
评论者:lxsameer
嘿,大家:

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

目前这是一个草案,我想在投入更多时间之前知道其他人对它的看法。请在我的仓库上查看

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

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

祝好
通过

评论者:lxsameer

嘿,大家:

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

0投票
目前这是一个草案,我想在投入更多时间之前知道其他人对它的看法。请在我的仓库上查看

评论者:mpenet

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

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

  • 祝好

  • 通过

  • 再次阅读几年前创建此问题的详细信息后,我现在实际上同意了Stu和Ghadi的评论,我认为我对core.async go内部的理解现在要好得多,在这个级别(以及回调)的线程仅仅是“管理”/上下文切换,因此对这些有更精细的控制并不那么重要。我认为我可能受了manifold设计的启发,它在某种程度上差异很大(以及其他方面),但这两个库相当不同。

0投票
我认为仍然有必要有一个更好的线程调用函数,以及可能另一个像thread一样的宏,它可以接受用户提供的管理器。
将允许传递执行器给tread-call的补丁考虑在内吗?
新的线程调用大小参数的宏版可能是什么形式(如果它是某种需要的东西;我认为一个新的宏更有意义,因为thread接受一个可能有一些困难来翻修那里的& &body)。
...