How does one start a thread in Clojure?

后端 未结 7 717
你的背包
你的背包 2021-01-30 10:49

I\'ve read a lot about how great Clojure is when it comes to concurrency, but none of the tutorials I\'ve read actually explain how to create a thread. Do you just do (.start (T

相关标签:
7条回答
  • 2021-01-30 11:01

    Usually when I want to start a thread in Clojure I just use future.

    As well as being simple to use, this has the advantage that you avoid having to do any messy Java interop to access the underlying Java threading mechanisms.

    Example usage:

    (future (some-long-running-function))
    

    This will execute the function asynchronously in another thread.

    (def a (future (* 10 10)))
    

    If you want to get the result, just dereference the future, e.g:

    @a
    => 100
    

    Note that @a will block until the future thread has completed its work.

    0 讨论(0)
  • 2021-01-30 11:02

    Yes, the way that you start a Java Thread in Clojure is something like what you have there.

    However, the real question is: why would you want to do that? Clojure has much better concurrency constructs than threads.

    If you look at the canonical concurrency example in Clojure, Rich Hickey's ant colony simulation, you will see that is uses exactly 0 threads. The only reference to java.lang.Thread in the entire source is three calls to Thread.sleep, whose sole purpose is to slow the simulation down so that you can actually see what is going on in the UI.

    All the logic is done in Agents: one agent for every ant, one agent for the animation and one agent for the pheromone evaporation. The playing field is a transactional ref. Not a thread nor lock in sight.

    0 讨论(0)
  • 2021-01-30 11:03

    Using a future is usually the simplest adhoc access to threading. Depends entirely on what you want to do :)

    0 讨论(0)
  • 2021-01-30 11:17

    Just to add my two cents (7 years later): Clojure functions implement the IFn interface that extends Callable as well as Runnable. Hence, you can simply pass them to classes like Thread.

    If your project might already uses core.async, I prefer using the go macro:

    (go func)
    

    This executes func in a super lightweight IOC (inversion of control) thread:

    go [...] will turn the body into a state machine. Upon reaching any blocking operation, the state machine will be 'parked' and the actual thread of control will be released. [...] When the blocking operation completes, the code will be resumed [...]

    In case func is going to do I/O or some long running task, you should use thread which is also part of core.async (check out this excellent blog post):

    (thread func)
    

    Anyway, if you want to stick to the Java interop syntax, consider using the -> (thread/arrow) macro:

    (-> (Thread. func) .start)
    
    0 讨论(0)
  • 2021-01-30 11:19

    Programming Clojure doesn't address that question until page 167: "Use Agents for Asynchronous Updates".

    Before you go starting threads, please note that Clojure will multitask on its own, given half a chance. I've written programs blithely ignorant of concurrency and found that when conditions are right, they occupy more than one CPU. I know that's not a very rigorous definition: I haven't explored this in depth yet.

    But for those occasions when you really do need an explicit separate activity, one of Clojure's answers is apparently the agent.

    (agent initial-state)

    will create one. It's not like a Java Thread in terms of being a code block waiting to be executed. Instead, it's an activity waiting to be given work to do. You do this via

    (send agent update-fn & args)

    The example does

    (def counter (agent 0))

    counter is your name and handle for the agent; the agent's state is the number 0.

    Having set that up, you can send work to the agent:

    (send counter inc)

    will tell it to apply the given function to its state.

    You can later pull the state out of the agent by dereferencing it:

    @counter will give you the current value of the number that started out at 0.

    Function await will let you do something like a join on the agent's activity, should it be a long one:

    (await & agents) will wait until they're all done; there's also another version that takes a timeout.

    0 讨论(0)
  • 2021-01-30 11:27

    Clojure fns are Runnable so it's common to use them in exactly the way you posted, yes.

    user=> (dotimes [i 10] (.start (Thread. (fn [] (println i)))))
    0                                                             
    1                                                             
    2                                                             
    4                                                             
    5                                                             
    3                                                             
    6                                                             
    7                                                             
    8                                                             
    9                                                             
    nil
    

    Another option is to use agents, in which case you would send or send-off and it'll use a Thread from a pool.

    user=> (def a (agent 0))
    #'user/a
    user=> (dotimes [_ 10] (send a inc))
    nil
    ;; ...later...
    user=> @a
    10
    

    Yet another option would be pcalls and pmap. There's also future. They are all documented in the Clojure API.

    0 讨论(0)
提交回复
热议问题