Timer Clojure可取消计时器
我遇到了一个我没有经验解决的问题 我需要像Clojure中带有状态的计时器这样的东西,它可以接收消息并进行累积 我在等5秒钟 例如,当我收到发送给该计时器的:[:left id]消息时,应该取消该消息,并使用该id执行一些操作。当我收到[:entered id]消息时,我应该累计它(如果收到的id少于5个)。当我收到5:输入的不同id的消息时,我应该执行一个操作并取消计时器,否则当输入的id不足时,在5秒钟后执行其他操作 我的朋友建议我使用频道,但我们没有设法用频道实现这一点 主要的问题是计时器应该有它的内部状态,应该保存在某个地方,但是当你使用“go”时,你不能这样做,只需要执行一些函数Timer Clojure可取消计时器,timer,concurrency,clojure,channel,Timer,Concurrency,Clojure,Channel,我遇到了一个我没有经验解决的问题 我需要像Clojure中带有状态的计时器这样的东西,它可以接收消息并进行累积 我在等5秒钟 例如,当我收到发送给该计时器的:[:left id]消息时,应该取消该消息,并使用该id执行一些操作。当我收到[:entered id]消息时,我应该累计它(如果收到的id少于5个)。当我收到5:输入的不同id的消息时,我应该执行一个操作并取消计时器,否则当输入的id不足时,在5秒钟后执行其他操作 我的朋友建议我使用频道,但我们没有设法用频道实现这一点 主要的问题是计时器
提前感谢。我建议使用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])