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

欢迎!请访问关于页面以了解更多关于如何使用本页面的信息。

0 投票
core.async

目前,core.async使用的线程池被定义为主要的全局值,它们在其对应的线程池中被所有或线程宏共享。

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 !)拥有的简单设置器,因为它一次只能使用一个池。
一个可能的解决方案可能是通过线程调用(使用与go调用相同类型的签名)的选项映射参数来完成,或者通过其他用于go块的机制。将该参数作为映射还有助于在需要时进一步扩展内部功能,而无需对API进行重大更改。

12 答案

0 投票

评论者:mpenet

我在这里有一份补丁 https://github.com/mpenet/core.async/commit/1c2b0a9af9a5e891af0f1631b8debd337a73999d 用来添加这些特性。

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

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

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

我将很快将.patch文件附加到问题跟踪中。

0 投票

评论者:mpenet

实际上,这个补丁是不完整的,我刚刚发现"ioc-macros"的重写会导致对全局线程池的调用(特别是通过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) 回调需要唤醒挂起的go-blocks。
2) 运行更重的thread(这可能会被阻塞,因此不能与相同的池共享)

目前,通道回调不知道它在唤醒什么。我认为这是一个好设计。如果它们唤醒一个线程,它们只是间接地通过传递一个promise来实现(链接:1)
我认为通道.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被调用的一些情况下,即函数是在全局定义的threadpool中以core.async除了thread之外的一切方式运行的情况下,可以 ***可选地 通过提供的threadpool函数参数来完成。默认值未改变,如果你不传递执行器,你将获得目前没有任何改变的master core.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的案例吗?

关于这个问题有没有开发者的讨论列表?

0 投票

评论者:mpenet

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

如果你读过补丁,或者查看过渠道的源代码,你会看到
渠道在其线程池上运行回调。

当这个补丁被提交时,我和Timothy Baldridge在IRC上花了一些时间讨论,
并根据他的建议做了一些修改。既然你
可能和他在一个办公室工作,我建议你和他讨论当前
的方法,因为他比我们任何一个人都更熟悉代码库。

0 投票

评论者:lxsameer

大家好,

我为线程池命名空间添加了一些功能,以便允许用户为主线程池和用于thread宏的那个提供执行器。

现在这还是一个草稿,我想在花更多时间之前了解其他人的意见。请查看
我的GitHub仓库上的提交:

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

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

再见

0 投票

评论者:mpenet

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

我认为仍然有必要有一个更好的线程调用函数,以及可能另一个像thread一样的宏,它接受用户提供的执行器。

  • 增加参数到tread-call以允许传入执行器的补丁会被考虑吗?

  • 那个新线程调用参数的宏版本可以采取什么形式(如果这被期望;我认为新的宏会更合适,因为thread接受一个&body,这可能有些难以重装)。

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

0 投票
...