multithreading 如何在 Clojure 中启动一个线程?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1768567/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 01:05:25  来源:igfitidea点击:

How does one start a thread in Clojure?

multithreadingconcurrencyclojure

提问by andrewdotnich

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 在并发方面的出色之处,但是我读过的教程中没有一个真正解释了如何创建线程。你只是做 (.start (Thread. func)) 还是我错过的另一种方式?

采纳答案by Brian Carper

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

Clojure的fns为Runnable所以它是常见的正是你张贴的方式来使用他们,是的。

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 sendor send-offand it'll use a Thread from a pool.

另一种选择是使用agents,在这种情况下,您将sendsend-off它将使用池中的 Thread 。

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

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

另一种选择是pcallspmap。还有future。它们都记录在Clojure API 中

回答by mikera

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 将阻塞,直到未来的线程完成其工作。

回答by Carl Smotricz

Programming Clojuredoesn'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)

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

(send agent update-fn & args)

The example does

这个例子确实

(def counter (agent 0))

(def counter (agent 0))

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

counter是您的姓名和代理人的姓名;代理的状态是数字 0。

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

设置完成后,您可以将工作发送给代理:

(send counter inc)

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

您可以稍后通过取消引用来将状态从代理中提取出来:

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

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

Function awaitwill let you do something like a joinon the agent's activity, should it be a long one:

函数await会让你join对代理的活动做一些类似的事情,如果它是一个很长的活动:

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

(await & agents)会等到他们都完成;还有另一个版本需要超时。

回答by J?rg W Mittag

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

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

However, the realquestion is: why would you want to do that? Clojure has muchbetter 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.Threadin the entire source is three calls to Thread.sleep, whose sole purpose is to slow the simulation down so that you can actually seewhat is going on in the UI.

如果您查看 Clojure 中的规范并发示例,Rich Hickey 的蚁群模拟,您会看到它恰好使用了 0 个线程。java.lang.Thread整个源代码中对的唯一引用是对 的三个调用Thread.sleep,其唯一目的是减慢模拟速度,以便您可以实际查看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.

所有的逻辑都在 Agents 中完成:每只蚂蚁一个代理,一个动画代理和一个信息素蒸发代理。比赛场地是一个交易参考。不是线程也不是锁。

回答by beatngu13

Just to add my two cents (7 years later): Clojure functions implement the IFninterfacethat extends Callableas 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 gomacro:

如果您的项目可能已经使用core.async,我更喜欢使用go宏:

(go func)

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

func在超轻量级IOC(控制反转)线程中执行

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 [...]

go[...] 将把身体变成一个状态机。在达到任何阻塞操作时,状态机将被“停放”并且实际的控制线程将被释放。[...] 当阻塞操作完成时,代码将被恢复 [...]

In case funcis going to do I/O or some long running task, you should use threadwhich is also part of core.async (check out thisexcellent blog post):

如果func要执行 I/O 或一些长时间运行的任务,您应该使用thread它也是 core.async 的一部分(查看这篇优秀的博客文章):

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

回答by Timothy Pratley

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

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

回答by Lars Bohl

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

(future f)宏包装在一个Callable形式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.Thread 对象的引用,以将其用作 java.lang.Runtime 关闭钩子,您可以像这样创建一个线程:

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

这还不会启动线程,只会创建它。要创建和运行线程,请将其提交到线程池或对其调用 .start :

(->
 (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.

布赖恩斯的回答也创建了一个线程,但不需要代​​理,所以非常优雅。另一方面,通过使用代理,我们可以避免创建 Callable。