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) 卡住是最好遇到的问题,因为你可以插入打印并找出什么是挂起的。次优平衡要得到清晰度更困难。

(3) 越简单越好。

非常感谢您的帮助!您对(1)的解释正是我所需要的。这似乎很重要,至少需要了解一下底层事件序列。我最初的心理模型是`core.async`提供了一种声明性API。

关于您关于使用打印来识别问题的观点(2),我同意这听起来很理想,但我不确定如何实现。如果我把这个问题理解正确,我们应该能够在代码中添加一个打印语句,在挂起之前打印出“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))))
在粗略层面上,保持你的原始算法,你可以在每个``之前/之后放入一个打印,以判断哪个导致了阻塞。
...