如何在Clojure中启动线程?

时间:2021-06-28 20:40:25

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 (Thread. func)), or is there another way that I've missed?

我读过很多关于Clojure在并发性方面有多棒的文章,但是我读过的教程中没有一个真正解释了如何创建线程。你就这么做吗?开始(线程。还是有其他我错过的方式?

7 个解决方案

#1


37  

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

Clojure fns是可运行的,所以用你发布的方式使用它们很常见,是的。

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.

另一个选择是pcall和pmap。还有未来。它们都记录在Clojure API中。

#2


31  

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

通常当我想在Clojure中启动一个线程时,我只使用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.

除了使用简单之外,这还有一个优势,即您可以避免使用任何凌乱的Java互操作来访问底层Java线程机制。

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.

注意,@a将阻塞,直到将来的线程完成它的工作。

#3


14  

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

编程Clojure直到第167页才解决这个问题:“使用代理进行异步更新”。

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.

在开始线程之前,请注意Clojure将自己完成多个任务,只要有一半的机会。我编写的程序对并发性一无所知,并且发现当条件合适时,它们会占用多个CPU。我知道这不是一个非常严格的定义:我还没有深入研究过这个问题。

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

但是当你确实需要一个明确的独立活动时,很明显Clojure的答案之一就是代理。

(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

将创建一个。它不像Java线程,因为它是一个等待执行的代码块。相反,它是一个等待被赋予工作去做的活动。你这样做通过

(send agent update-fn & args)

(发送代理更新-fn & args)

The example does

这个例子

(def counter (agent 0))

(def计数器(代理0))

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

柜台是你的名字和代理的柄;代理的状态是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.

@counter将为您提供从0开始的数字的当前值。

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

函数waiting会让您在代理的活动上做一些类似join的事情,如果是长函数的话:

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

(等待和代理)会等到他们都完成;还有另一个需要超时的版本。

#4


7  

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

是的,在Clojure中启动Java线程的方式与您所拥有的方式类似。

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

然而,真正的问题是:你为什么要这么做?Clojure具有比线程更好的并发结构。

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.

如果您查看Clojure中的规范并发示例,Rich Hickey的ant集群模拟,您将看到它正好使用了0个线程。对java.lang的惟一引用。整个源中的线程是对线程的三个调用。睡眠,它的唯一目的是降低模拟速度,这样你就可以看到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.

所有的逻辑都在代理中完成:每只蚂蚁一个代理,动画一个代理,信息素蒸发一个代理。竞技场是一个事务性的裁判,不是一个线程,也不是一个锁。

#5


2  

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

使用future通常是对线程的最简单的临时访问。完全取决于你想做什么:

#6


2  

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.

再补充一点(7年后):Clojure函数实现了IFn接口,该接口扩展了Callable和Runnable。因此,您可以简单地将它们传递给诸如Thread之类的类。

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

如果您的项目可能已经使用了core。异步,我更喜欢使用go宏:

(go func)

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

这在一个超轻量的IOC(控制反转)线程中执行func:

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):

如果func要执行I/O或一些长时间运行的任务,您应该使用线程,这也是核心的一部分。异步(查看这篇优秀的博客文章):

(thread func)

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

无论如何,如果您想坚持使用Java互操作语法,请考虑使用-> (thread/arrow)宏:

(-> (Thread. func) .start)

#7


0  

The (future f) macro wraps the form f in a Callable (via fn*) and submits that to a thread pool immediately.

宏(future f)将表单f封装在一个可调用的(通过fn*)中,并立即将其提交给线程池。

if you need a reference to a java.lang.Thread object, for instance, to use it as a java.lang.Runtime shutdown hook, you can create a Thread like this:

如果您需要引用一个java.lang。例如,将线程对象用作java.lang。运行时关机挂钩,您可以创建如下线程:

(proxy [Thread] [] (run [] (println "running")))

This will not start the thread yet, only create it. To create and run the thread, submit it to a thread pool or call .start on it:

这将不会启动线程,只创建它。要创建并运行线程,请将其提交到线程池或调用。

(->
 (proxy [Thread] [] (run [] (println "running")))
 (.start))

Brians's answer also creates a thread but doesn't need proxy, so that's very elegant. On the other hand, by using proxy we can avoid creating a Callable.

Brians的答案也创建了一个线程,但不需要代理,所以非常优雅。另一方面,通过使用代理,我们可以避免创建可调用的。

#1


37  

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

Clojure fns是可运行的,所以用你发布的方式使用它们很常见,是的。

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.

另一个选择是pcall和pmap。还有未来。它们都记录在Clojure API中。

#2


31  

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

通常当我想在Clojure中启动一个线程时,我只使用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.

除了使用简单之外,这还有一个优势,即您可以避免使用任何凌乱的Java互操作来访问底层Java线程机制。

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.

注意,@a将阻塞,直到将来的线程完成它的工作。

#3


14  

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

编程Clojure直到第167页才解决这个问题:“使用代理进行异步更新”。

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.

在开始线程之前,请注意Clojure将自己完成多个任务,只要有一半的机会。我编写的程序对并发性一无所知,并且发现当条件合适时,它们会占用多个CPU。我知道这不是一个非常严格的定义:我还没有深入研究过这个问题。

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

但是当你确实需要一个明确的独立活动时,很明显Clojure的答案之一就是代理。

(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

将创建一个。它不像Java线程,因为它是一个等待执行的代码块。相反,它是一个等待被赋予工作去做的活动。你这样做通过

(send agent update-fn & args)

(发送代理更新-fn & args)

The example does

这个例子

(def counter (agent 0))

(def计数器(代理0))

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

柜台是你的名字和代理的柄;代理的状态是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.

@counter将为您提供从0开始的数字的当前值。

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

函数waiting会让您在代理的活动上做一些类似join的事情,如果是长函数的话:

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

(等待和代理)会等到他们都完成;还有另一个需要超时的版本。

#4


7  

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

是的,在Clojure中启动Java线程的方式与您所拥有的方式类似。

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

然而,真正的问题是:你为什么要这么做?Clojure具有比线程更好的并发结构。

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.

如果您查看Clojure中的规范并发示例,Rich Hickey的ant集群模拟,您将看到它正好使用了0个线程。对java.lang的惟一引用。整个源中的线程是对线程的三个调用。睡眠,它的唯一目的是降低模拟速度,这样你就可以看到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.

所有的逻辑都在代理中完成:每只蚂蚁一个代理,动画一个代理,信息素蒸发一个代理。竞技场是一个事务性的裁判,不是一个线程,也不是一个锁。

#5


2  

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

使用future通常是对线程的最简单的临时访问。完全取决于你想做什么:

#6


2  

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.

再补充一点(7年后):Clojure函数实现了IFn接口,该接口扩展了Callable和Runnable。因此,您可以简单地将它们传递给诸如Thread之类的类。

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

如果您的项目可能已经使用了core。异步,我更喜欢使用go宏:

(go func)

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

这在一个超轻量的IOC(控制反转)线程中执行func:

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):

如果func要执行I/O或一些长时间运行的任务,您应该使用线程,这也是核心的一部分。异步(查看这篇优秀的博客文章):

(thread func)

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

无论如何,如果您想坚持使用Java互操作语法,请考虑使用-> (thread/arrow)宏:

(-> (Thread. func) .start)

#7


0  

The (future f) macro wraps the form f in a Callable (via fn*) and submits that to a thread pool immediately.

宏(future f)将表单f封装在一个可调用的(通过fn*)中,并立即将其提交给线程池。

if you need a reference to a java.lang.Thread object, for instance, to use it as a java.lang.Runtime shutdown hook, you can create a Thread like this:

如果您需要引用一个java.lang。例如,将线程对象用作java.lang。运行时关机挂钩,您可以创建如下线程:

(proxy [Thread] [] (run [] (println "running")))

This will not start the thread yet, only create it. To create and run the thread, submit it to a thread pool or call .start on it:

这将不会启动线程,只创建它。要创建并运行线程,请将其提交到线程池或调用。

(->
 (proxy [Thread] [] (run [] (println "running")))
 (.start))

Brians's answer also creates a thread but doesn't need proxy, so that's very elegant. On the other hand, by using proxy we can avoid creating a Callable.

Brians的答案也创建了一个线程,但不需要代理,所以非常优雅。另一方面,通过使用代理,我们可以避免创建可调用的。