基于core.async walk through example,我在下面创建了类似的代码来处理一些使用多个通道且超时为10秒的CPU密集型作业。但是在主线程返回后,CPU使用率仍然保持在700%左右(8台CPU机器)。我必须在emacs中手动运行nrepl-close来关闭Java进程。
有没有正确的方法来杀死(go ..)块产生的宏线程?我试过了!每个陈,但它不起作用。我希望在主线程返回后确保Java进程的CPU使用率回到0。
(defn [] RETURNED-STR-FROM-SOME-CPU-INTENSE-JOB (do... (str ...)))
(let [n 1000
cs (repeatedly n chan)]
(doseq [c cs]
(go
(>! c (RETURNED-STR-FROM-SOME-CPU-INTENSE-JOB ))))
(dotimes [i n]
(let [[result source] (alts!! (conj cs (timeout 10000))) ] ;;wait for 10 seconds for each job
(if (list-contains? cs source) ;;if returned chan belongs to cs
(prn "OK JOB FINISHED " result)
(prn "JOB TIMEOUT")
)))
(doseq [i cs]
(close! i)) ;;not useful for "killing" macro thread
(prn "JOBS ARE DONE"))
;;Btw list-contains? function is used to judge whether an element is in a list
;;http://stackoverflow.com/questions/3249334/test-whether-a-list-contains-a-specific-value-in-clojure
(defn list-contains? [coll value]
(let [s (seq coll)]
(if s
(if (= (first s) value) true (recur (rest s) value))
false)))
答案 0 :(得分:2)
在REPL中似乎没有干净的方式。
我首先使用弃用的方法Thread.stop
尝试了一种非常脏的方法 (doseq [i @threadpool ]
(.stop i))
主线程返回REPL后,CPU使用率下降似乎有效,但如果我在REPL中再次运行程序,它只会挂在go块部分!
然后我用Google搜索并找到this blog,然后说
最后要注意的一点是:我们没有明确地做任何工作来关闭go例程。当主函数退出时,Go例程将自动停止操作。因此,go例程就像JVM中的守护程序线程(除了“线程”部分......)
所以我再次尝试将我的项目变成uberjar并在命令控制台上运行,结果当闪烁的光标返回控制台时,CPU使用率会立即下降!
答案 1 :(得分:1)
基于对另一个相关问题How to control number of threads in (go...)的回答,我找到了一种更好的方法来正确杀死由(go ...)块启动的所有线程:
首先更改执行程序var并提供自定义线程池
;; def, not defonce, so that the executor can be re-defined
;; Number of threads are fixed to be 4
(def my-executor
(java.util.concurrent.Executors/newFixedThreadPool
4
(conc/counted-thread-factory "my-async-dispatch-%d" true)))
(alter-var-root #'clojure.core.async.impl.dispatch/executor
(constantly (delay (tp/thread-pool-executor my-executor))))
然后在(go ...)块结束时调用.shutdownNow和.awaitTermination方法
(.shutdownNow my-executor)
(while (not (.awaitTermination my-executor 10 java.util.concurrent.TimeUnit/SECONDS ) )
(prn "...waiting 10 secs for executor pool to finish") )
<强> [UPDATE] 强> 上面的关闭执行程序方法似乎不够纯粹。我的最终解决方案是使用thunk-timeout函数将控制其自身超时的函数发送到go块。积分转到this post。以下示例
(defn toSendToGo [args timeoutUnits]
(let [result (atom nil)
timeout? (atom false)]
(try
( thunk-timeout
(fn [] (reset! result (myFunction args))) timeoutUnits)
(catch java.util.concurrent.TimeoutException e (do (prn "!Time out after " timeoutUnits " seconds!!") (reset! timeout? true)) ))
(if @timeout? (do sth))
@result))
(let [c ( chan)]
(go (>! c (toSendToGo args timeoutUnits))))
答案 2 :(得分:1)
(shutdown-agents)
特定于实现的JVM:agents和通道都使用全局线程池,代理的终止函数迭代并关闭VM中的所有开放线程。 首先清空频道:此操作是立即且不可逆的(特别是如果您在REPL中)。