Timer Clojure可取消计时器

Timer Clojure可取消计时器,timer,concurrency,clojure,channel,Timer,Concurrency,Clojure,Channel,我遇到了一个我没有经验解决的问题 我需要像Clojure中带有状态的计时器这样的东西,它可以接收消息并进行累积 我在等5秒钟 例如,当我收到发送给该计时器的:[:left id]消息时,应该取消该消息,并使用该id执行一些操作。当我收到[:entered id]消息时,我应该累计它(如果收到的id少于5个)。当我收到5:输入的不同id的消息时,我应该执行一个操作并取消计时器,否则当输入的id不足时,在5秒钟后执行其他操作 我的朋友建议我使用频道,但我们没有设法用频道实现这一点 主要的问题是计时器

我遇到了一个我没有经验解决的问题

我需要像Clojure中带有状态的计时器这样的东西,它可以接收消息并进行累积

我在等5秒钟

例如,当我收到发送给该计时器的:[:left id]消息时,应该取消该消息,并使用该id执行一些操作。当我收到[:entered id]消息时,我应该累计它(如果收到的id少于5个)。当我收到5:输入的不同id的消息时,我应该执行一个操作并取消计时器,否则当输入的id不足时,在5秒钟后执行其他操作

我的朋友建议我使用频道,但我们没有设法用频道实现这一点

主要的问题是计时器应该有它的内部状态,应该保存在某个地方,但是当你使用“go”时,你不能这样做,只需要执行一些函数


提前感谢。

我建议使用Java
计时器和atom来累积状态。下面是一个您可以做的示例:

(def timer (Timer.))
(def state (atom [] ))
(def timer-delay 5000) ; millis

(def timertask
  (proxy [TimerTask] []
    (run []
      (println "timer done")
      (println "state = " @state)
      )))

(defn start-timer []
  (println "starting timer")
  (.schedule timer timertask timer-delay))

(defn add-to-state [item]
  (Thread/sleep 500)
  (println "  adding:" item)
  (swap! state conj item))

(start-timer)
(Thread/sleep 2000)
(add-to-state :first)
(add-to-state :second)
(add-to-state :third)
输出:

starting timer
  adding: :first
  adding: :second
  adding: :third

timer done
state =  [:first :second :third]

看起来您想要的是
java.util.concurrent
包中的
ScheduledExecutorService
s。它们易于设置,并且可以停止和重置,无需太多麻烦。与Clojure的互操作也非常简单。(在我看来,这比在Java中使用它们更容易。)

下面是一个使用
SingleThreadScheduledExecutor
设置的示例,设置为每五秒钟生成一次定期调用

(ns cncltimer.core
(:gen类)
(:导入(java.util.concurrent Executors ScheduledExecutorService)
计划的未来时间单位)
(jline终端)
(def executor(Executors/newSingleThreadScheduledExecutor))
(def fyutchur(原子为零))
(def状态(原子[])
(defn每五秒钟运行一次
“高亮显示指定选项卡的内容。”
[]
(具体化Runnable)
(运行[此]
(打印“五秒钟过去了”。)
(如果需要,定义取消未来
“取消未来,如果它存在。不要中断
如果任务已启动,则返回该任务。“
[]
(何时(而非(无?@fyutchur))
(不是(.isDone@fyutchur)))
(.cancel@fyutchur false)))
(定义计划执行人)
“如果需要,取消任何现有期货,并重置为
新的延迟期。”
[]
(如果需要,取消未来)
(重置!fyutchur(.scheduleAtFixedRate执行器)(每五秒钟运行一次)
5(时间单位/秒)
(defn)关闭执行器
“从运行中取消任何期货并关闭
计划运行的执行器。”
[]
(如果需要,取消未来)
(.关闭现在执行人)
; 如果与lein一起跑步,请使用“lein蹦床跑步”
(定义-主参数[&args]
(重新安排执行人)
(let[t(终端/getTerminal)]
(循环[k(.readCharacter t System/in)]
(如果(=k 75)
(停机执行器)
(做
(当(=k 13)
(swap!state conj k)
(println“@state:@state))
(如果(和(=k 13)
(>=(州计数)5)
(做
(打印“一个动作”)
(停机执行器)
(重复(.readCharacter t System/in()(())())))
您可以使用命令行
lein trampoline run
与leiningen一起运行此命令,并从键盘输入一些数据。如果你什么也不做,它只会继续打印“五秒钟过去了”。随时按
Enter
,将数据累积到状态变量中。按
左箭头
关闭计时器并退出。其他任何事情都应该被忽略。每五秒钟运行一次,直到程序累积5个“回车”事件或用左箭头键停止


jline
东西(我使用的是0.9.94版)在演示中仅用于键盘处理。你真正的程序可能不需要它。同样地,使用蹦床也是为了正确操作键盘。您的程序可能不需要它。

这里还有一个蓝图:

(defn make-timer [sec exit-fn enter5-fn]
  (let [data (atom [])
        five-entered? (fn [] (->> @data
                                  (filter #(= (first %)))
                                  (map second)
                                  distinct
                                  count
                                  (<= 5)))]
    (future
      (loop []
        (println :tick @data)
        (if-let [[_ exit-id] (some #(= (first %) :exit) @data)]
          (future (exit-fn exit-id))
          (if (five-entered?)
            (future (enter5-fn))
            (do (Thread/sleep (* 1000 sec))
                (recur))))))
    #(swap! data conj %)))

可能您应该尝试使用通道来实现这一点,方案几乎是一样的:
make timer
将返回新通道,您可以在其中输入值,并每隔五秒钟从那里读取数据(使用
alt!
此通道和
timeout
通道)

谢谢,但我对clojure-only解决方案更感兴趣。我知道如何用Java实现这一点。谢谢,但我对clojure唯一的解决方案更感兴趣。使用Java,我知道如何实现这一点。
(def send! (make-timer 5
                       (partial println :exit)
                       (partial println :entered-5)))

(send! [:exit 100])
;; after 5 seconds prints ":exit 100"
;; the same works for multiple (send! [:enter id])