J'ai lu beaucoup de choses sur l'excellence de Clojure en matière de concurrence, mais aucun des tutoriels que j'ai lus n'explique réellement comment créer un thread. Faut-il simplement faire (.start (Thread. func)), ou existe-t-il un autre moyen que j'ai manqué ?
Réponses
Trop de publicités?Clojure fn
sont Runnable
il est donc courant de les utiliser exactement comme vous l'avez indiqué, oui.
user=> (dotimes [i 10] (.start (Thread. (fn [] (println i)))))
0
1
2
4
5
3
6
7
8
9
nil
Une autre option consiste à utiliser agents Dans ce cas, vous devez send
o send-off
et il utilisera un Thread d'un pool.
user=> (def a (agent 0))
#'user/a
user=> (dotimes [_ 10] (send a inc))
nil
;; ...later...
user=> @a
10
Une autre option encore serait pcalls
y pmap
. Il y a aussi future
. Ils sont tous documentés dans le API Clojure .
Habituellement, lorsque je veux démarrer un fil de discussion dans Clojure, j'utilise simplement futur .
En plus d'être simple à utiliser, cette méthode présente l'avantage d'éviter d'avoir à effectuer une interopérabilité Java désordonnée pour accéder aux mécanismes de threading Java sous-jacents.
Exemple d'utilisation :
(future (some-long-running-function))
Cela permettra d'exécuter la fonction de manière asynchrone dans un autre thread.
(def a (future (* 10 10)))
Si vous voulez obtenir le résultat, il suffit de déréférencer le futur, par ex :
@a
=> 100
Notez que @a se bloque jusqu'à ce que le futur thread ait terminé son travail.
Programmation de Clojure n'aborde pas cette question avant la page 167 : "Utiliser des agents pour les mises à jour asynchrones".
Avant de vous lancer dans des discussions, sachez que Clojure est capable de faire du multitâche tout seul, si on lui en donne l'occasion. J'ai écrit des programmes ignorant allègrement la concurrence et j'ai constaté que lorsque les conditions sont réunies, ils occupent plus d'une unité centrale. Je sais que ce n'est pas une définition très rigoureuse : Je n'ai pas encore exploré ce sujet en profondeur.
Mais pour les occasions où vous avez vraiment besoin d'une activité séparée explicite, l'une des réponses de Clojure est apparemment l'agent.
(agent initial-state)
en créera un. Ce n'est pas comme un thread Java, c'est-à-dire un bloc de code qui attend d'être exécuté. Il s'agit plutôt d'une activité qui attend qu'on lui donne du travail à faire. Pour ce faire, il suffit de
(send agent update-fn & args)
L'exemple fait
(def counter (agent 0))
counter
est votre nom et votre identifiant pour l'agent ; l'état de l'agent est le numéro 0.
Une fois cette étape franchie, vous pouvez envoyer votre travail à l'agent :
(send counter inc)
lui dira d'appliquer la fonction donnée à son état.
Vous pouvez ensuite extraire l'état de l'agent en le déréférençant :
@counter
vous donnera la valeur actuelle du nombre qui a commencé à 0.
Fonction await
vous permettra de faire quelque chose comme un join
sur l'activité de l'agent, si elle est longue :
(await & agents)
attendra qu'ils soient tous terminés ; il existe aussi une autre version qui prend un délai d'attente.
Oui, la façon dont vous démarrez un Thread Java dans Clojure est quelque chose comme ce que vous avez là.
Cependant, le réel La question est : pourquoi voulez-vous faire cela ? Clojure a beaucoup de meilleures constructions de concurrence que les threads.
Si vous regardez l'exemple canonique de la concurrence dans Clojure, La simulation de la colonie de fourmis de Rich Hickey vous verrez qu'il utilise exactement 0 fils. La seule référence à java.lang.Thread
dans toute la source est de trois appels à Thread.sleep
dont le seul but est de ralentir la simulation pour que vous puissiez réellement voir ce qui se passe dans l'interface utilisateur.
Toute la logique est réalisée en Agents : un agent pour chaque fourmi, un agent pour l'animation et un agent pour l'évaporation des phéromones. Le terrain de jeu est un réf. transactionnel. Pas un thread ni un verrou en vue.