【问题标题】:Clojure Running Multiple Threads with Functions with zero or one inputClojure 使用零个或一个输入的函数运行多个线程
【发布时间】:2021-01-22 21:04:22
【问题描述】:

我正在 Clojure 中尝试运行独立线程,但我遇到了我不理解的不同行为。

对于我的代码编辑器,我使用的是 Atom(不是 emacs),REPL 是 Chlorine。

我正在测试一个非常简单的函数,它只打印数字。 这个从 100 打印到 1 并且不接受任何输入:

(defn pl100 []
  "pl100 = Print Loop from 100 to 1"
  (loop [counter 100]
    (when (pos? counter)
      (do
        (Thread/sleep 100)
        (println (str "counter: " counter))
        (recur (dec counter))))))

除了需要输入之外,这个功能完全相同:

(defn pl-n [n]
    "pl-n = Print Loop from n to 1"
    (loop [counter n]
      (when (pos? counter)
        (do
          (Thread/sleep 100)
          (println (str "counter: " counter))
          (recur (dec counter))))))

当我使用时

(.start (Thread. #(.run pl100)))
; --> prints to console REPL
; --> runs with no errors

这段代码

  1. 打印到控制台 REPL(我称之为 lein)和
  2. 运行没有错误

当我使用时

(.start (Thread. #(.run (pl-n 100))))
; prints to console REPL
; --> java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "target" is null

这段代码

  1. 打印到控制台 REPL
  2. 以上述异常结束

当我使用时

(.start (Thread. pl100))
; --> prints to the console REPL
; --> runs with no errors  

这段代码

  1. 打印到控制台 REPL
  2. 运行没有错误

当我使用时

(.start (Thread. (pl-n 100)))
; --> prints to Atom REPL, not console REPL!
; ends with exception 
; Execution error (NullPointerException) at java.lang.Thread/<init> (Thread.java:396).
; name cannot be null
; class java.lang.NullPointerException

这段代码

  1. 打印到 Atom REPL(我使用的是 Atom,而不是 emacs)!不像其他人那样去控制台 REPL
  2. 以异常结束

那么,谁能帮我理解一下:

  1. 为什么当我运行一个接受输入的函数时,Java 会出错?为什么函数调用不等价?
  2. (.run ...) 在做什么?
  3. 为什么有时代码会打印到控制台,而有时会打印到 Atom/Chlorine?

【问题讨论】:

    标签: multithreading concurrency clojure


    【解决方案1】:

    简单回答:Thread.run 需要一个函数。您的第一个展览为其提供了一个功能,pl100,并且可以按您的预期工作:

    #(.run pl100)
    

    如果您给.run 提供的不是函数,而是调用返回的值 pl100 函数,将会发生完全不同的事情。事实上,pl100 返回 nil,所以Thread.run 会抛出 NullPointerException:

    #(.run (pl100)) ;; NullPointerException
    

    这就解释了为什么您的第二个展览没有达到您的预期。 pl-n 返回 nil,然后你将 nil 传递给 Thread.run 时出现异常:

    #(.run (pl-n 100)) ;; NullPointerException
    

    为了弥合差距 - 在需要无参数函数的 Thread.run 和需要参数的函数 pl-n 之间,您可以引入无参数函数(以满足 Thread.run)使用所需的参数调用 pl-n。习惯上这将是一个匿名函数。不幸的是,您不能将#() 嵌套在#() 中,因此您必须对其中一个匿名函数使用更冗长的(fn [] ...) 语法,很可能是外部函数。

    【讨论】:

      【解决方案2】:

      试试这样的:

      (ns tst.demo.core
        (:use demo.core tupelo.core tupelo.test)
        (:require [tupelo.string :as str]))
      
      (defn pl100 []
        "pl100 = Print Loop from 100 to 1"
        (loop [counter 10]
          (when (pos? counter)
            (do
              (Thread/sleep 100)
              (println (str "pl100 counter: " counter))
              (recur (dec counter))))))
      
      
      (defn pl-n [n]
        "pl-n = Print Loop from n to 1"
        (loop [counter n]
          (when (pos? counter)
            (do
              (Thread/sleep 100)
              (println (str "pl-n counter: " counter))
              (recur (dec counter))))))
      
      (dotest
        (newline)
        (.start (Thread. pl100))
        (Thread/sleep (* 2 1000))
      
        (newline)
        (.start (Thread. #(pl-n 5)))
        (Thread/sleep (* 2 1000))
        (newline)
        (println :done)
        )
      

      Clojure 函数已经是 Runnable 的一个实例,您不需要 #(.run xxx) 语法。结果:

          --------------------------------------
             Clojure 1.10.2-alpha1    Java 15
          --------------------------------------
          
          Testing tst.demo.core
          
          pl100 counter: 10
          pl100 counter: 9
          pl100 counter: 8
          pl100 counter: 7
          pl100 counter: 6
          pl100 counter: 5
          pl100 counter: 4
          pl100 counter: 3
          pl100 counter: 2
          pl100 counter: 1
          
          pl-n counter: 5
          pl-n counter: 4
          pl-n counter: 3
          pl-n counter: 2
          pl-n counter: 1
          
          :done
      

      为了更简单,只需使用 Clojure future

        (future (pl100))
        (Thread/sleep (* 2 1000))
      
        (newline)
        (future (pl-n 5))
        (Thread/sleep (* 2 1000))
        (newline)
        (println :done)
      

      如果您删除Thread/sleep,您可以看到它们并行运行:

        (future (pl100))
        (future (pl-n 5))
        (Thread/sleep (* 2 1000))
        (newline)
        (println :done)
      

      结果

      pl100 counter: 10
      pl-n counter: 5
      pl100 counter: 9
      pl-n counter: 4
      pl100 counter: 8
      pl-n counter: 3
      pl100 counter: 7pl-n counter: 2
      
      pl100 counter: 6pl-n counter: 1
      
      pl100 counter: 5
      pl100 counter: 4
      pl100 counter: 3
      pl100 counter: 2
      pl100 counter: 1
      

      【讨论】:

        猜你喜欢
        • 2022-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-25
        相关资源
        最近更新 更多