You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Clojure core.async实现睡眠理发师问题的报错与返回值求助

问题根因
  • 空指针报错并非来自customer-num的nil判断:operate-shop函数里在let块外层多套了一层括号,let块执行完关闭通道的逻辑后返回值为nil,外层括号会尝试将nil作为函数调用,直接触发IFn.invoke空指针异常,只是报错时序刚好和理发师协程的nil判断行重合导致误判。
  • 无法返回理发总次数的原因:cut-hair的返回值是core异步协程对应的channel,最终统计的理发次数是写入这个channel的值,原代码没有保存这个channel引用,也没有读取channel内的结果,自然拿不到返回值。
  • 终止逻辑bug:原代码中alts!!返回的第二个值是被选中的channel对象,不是顾客生成协程本身,手动close!这个值没有实际的终止效果,还会干扰协程正常退出。
修复后完整代码
(ns sleepbarber
  (:require [clojure.core.async
             :as a
             :refer [>! <! >!! <!! go go-loop chan dropping-buffer close!
                     alts!! timeout]]))

(defn cut-hair [barber-shop]
  (go-loop [haircuts 0]
    (if-some [customer-num (<! barber-shop)]
      (do
        (<! (timeout 20)) ; 单次理发耗时20ms
        (println (str "已完成理发数:" haircuts ",累计流失顾客:" (- customer-num haircuts)))
        (recur (inc haircuts)))
      haircuts))) ; 通道关闭无新顾客时返回总理发数

(defn operate-shop [open-time]
  (let [barber-shop (chan (dropping-buffer 3)) ; 等候区3个座位
        barber-result-ch (cut-hair barber-shop) ; 保存理发师协程的结果通道
        customer-ch (go-loop [customer 0]
                      (<! (timeout (+ 10 (rand-int 20)))) ; 顾客到店间隔10~30ms随机
                      (>! barber-shop customer)
                      (recur (inc customer)))]
    ; 等待开店时长结束
    (alts!! [(timeout open-time)])
    ; 关店:停止接收新顾客,关闭店铺通道通知理发师收尾
    (close! barber-shop)
    (close! customer-ch)
    ; 等待理发师处理完排队顾客,返回最终理发总数
    (<!! barber-result-ch)))

; 运行示例:开店2秒,打印最终理发数
(println "开店2秒总计完成理发数:" (operate-shop 2000))
关键修改说明
  • 把barber-shop从全局变量改为函数内部创建的局部通道,避免多次运行时的状态污染
  • 移除了operate-shop中包裹let块的多余括号,从根源消除无意义的函数调用触发的空指针
  • 开店超时后先关闭店铺通道,理发师协程读取到nil时会自动退出循环,处理完队列中剩余的等候顾客后将总理发数写入结果通道
  • 显式读取理发师协程返回通道中的值,作为operate-shop的返回值传递给调用方
  • 用if-some替代手动nil?判断,写法更符合Clojure习惯,逻辑等价不会触发空指针

内容的提问来源于stack exchange,提问作者dsagman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 19:24:31