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

欢迎!请查看关于页面,了解更多信息。

0 投票
core.async

我是core.async(以及更普遍的CSP)的新手,并且难以找到好的调试策略。特别是,我经常无法诊断代码的哪一部分无限地阻塞主线程。我将提供一个特定的例子

(deftest passing-test
  (let [c (async/to-chan! [2 1 0 5 4])]
    (is (= [2 1 0 5 4]
           (async/<!! (async/into [] c))))))


(deftest hanging-test
  (let [[e o] (->> (async/to-chan! [2 1 0 5 4])
                   (async/split even?))]
    (is (= [2 0 4]
           (async/<!! (async/into [] e))))
    (is (= [1 5]
           (async/<!! (async/into [] o))))))

第一个测试通过且不会阻塞主线程。第二个测试一直挂起。唯一的区别是添加了async/split来将偶数和奇数整数路由到相应的通道。

以下是我的具体问题

  1. 是什么解释了第一个和第二个测试在阻塞主线程方面的差异?

  2. 有什么策略可以调试意外阻塞主线程的core.async代码?

  3. 编写创建和操作通道的代码的单元测试的最佳方式是什么?(async/<!! (async/into [] o))是否是收集通过通道流动的值的合理方式?

1 个答案

+1 投票

选出
 
最佳答案

(1) 第二次测试的生产者部分放入2,消费者(读取偶数)读取2;生产者放入1,但消费者尚未读取奇数,因此生产者等待。从消费者的角度来看,“偶数”通道“永远不会关闭”,因此消费者卡在第一个<!!处。

(2) 挂起(hang)是最好处理的问题,因为我们可以在其中插入打印信息来找出 hang 的原因。子优化的平衡则更难以清晰地理解。

(3) 简单就是好。

非常感谢您的帮助!您对(1)的解释正是我所需要的。看来理解(至少有一点)关于事件的基本顺序是很重要的。我最初的心理模型是,`core.async` 提供了一个声明式 API。

关于您关于使用打印来识别问题的观点,我同意这听起来很理想,但我不确定如何实现这一点。如果我们正确地理解了这个问题,我们是否应该能够在代码中某个地方添加一个打印语句,在挂起之前打印一个`2`?是这样的吗?您有一个这样的例子吗?

关于(3):阿门(同意这一点)。

非常感谢!
在以新的视角重新阅读一些资源后,我发现了一种添加调试打印语句的方法。我很乐意了解任何更简洁的方法。

  (let [[e o] (->> (async/to-chan! [2 1 0 5 4])
                     (async/split even?))]

      (async/go-loop []
        (when-let [x (async/<! e)]
          (println x)
          (recur))))
在没有修改原始算法的情况下,您可以在每个`<!!`之前/之后放置一个打印语句,以便确定哪个是阻塞的。
...