2017-12-27 15 views
0

Ich möchte einige Futures in Clojure erstellen und sie alle in einem bestimmten Thread ausführen, um sicherzustellen, dass sie einzeln ausgeführt werden. Ist das möglich?Wie man einige Clojure-Futures in einem einzigen Thread ausführt?

Es ist nicht schwer, die Java-Bibliotheken dafür einzupacken, aber bevor ich das tue, möchte ich sicherstellen, dass ich keine Clojure-Methode vermisse. In Java kann ich dies tun, indem ich FutureTask implementiere und diese Aufgaben an einen Singlethread-Executor übertrage.

+0

Warum würden Sie wollen Konstrukte schaffen, die für die Parallelität ausgelegt sind, nur um zu erzwingen, dass sie ausführen nicht gleichzeitig? Vielleicht haben Sie bereits vorhandene Aufgaben auf diese Weise definiert, oder Sie führen sie normalerweise gleichzeitig aus, möchten jedoch die sequenzielle Ausführung für eine begrenzte Zeit erzwingen? Nur neugierig. – Josh

+0

Angenommen, es gibt mehrere Threads (z. B. Threads auf einem Server anfordern), die Tasks ausführen möchten, die eine freigegebene Ressource X verwenden, die jeweils nur von einem Thread verwendet werden kann. Eine Lösung besteht darin, eine Sperre zu greifen, wenn auf X zugegriffen wird. Eine andere Möglichkeit besteht darin, Tasks (Futures) für einen mit X verknüpften Thread zu senden. Die anderen aufrufenden Threads warten möglicherweise nicht auf das Ergebnis der Zukunft. –

+0

Sicher, aber warum in diesem Fall überhaupt Threads erstellen? Warum nicht einfach die Aufgaben als Funktionen aus einem einzigen Thread aufrufen? – Josh

Antwort

2

Clojures future macro ruft future-call Funktion, die a dedicated executor service verwendet. Das bedeutet, dass Sie keine Kontrolle haben, um eine sequenzielle Ausführung zu erzwingen.

Auf der anderen Seite können Sie promise statt future Objekte und ein future Faden deliver die Ergebnisse, um sequentiell verwenden. Promise API ist ähnlich wie future s bieten. Sie haben deref und realized? auch.

Im folgenden Codebeispiel werden die Subtasks nacheinander auf einem neuen Thread im Hintergrund ausgeführt, während das unmittelbar zurückgegebene Ergebnis der Funktion die Versprechungen zu den berechneten Werten enthält.

(defn start-async-calc [] 
    (let [f1 (promise) 
     f2 (promise) 
     f3 (promise)] 
    (future 
     (deliver f1 (task-1)) 
     (deliver f2 (task-2)) 
     (deliver f3 (task-3))) 
    {:task1 f1 
    :task2 f2 
    :task3 f3})) 
1

wenn Sie die Anrufe future sequentialisieren möchten, können Sie es manuell wie folgt verwenden:

(do @(future 1) 
    @(future 2) 
    @(future 3)) 

sie würden möglicherweise noch in verschiedenen Threads aufgerufen, aber der nächste wird erst aufgerufen werden das vorherige ist beendet. Dies wird durch die Funktion @ (oder deref) garantiert. Dies bedeutet, dass der Thread, in dem Sie das Formular do ausführen, vor dem Abschluss mit "prev promise" blockiert wird und dann den nächsten spawnt.

Sie können es mit Makro wie folgt prettify:

(defmacro sequentialize [& futures] 
    `(do [email protected](map #(list `deref %) futures))) 

user> (let [a (atom 1)] 
     (sequentialize 
     (future (swap! a #(* 10 %))) 
     (future (swap! a #(+ 20 %))) 
     (future (swap! a #(- %)))) 
     @a) 
;;=> -30 

dies tut genau das gleiche wie manuelle do. Beachten Sie, dass Mutationen a Atom sind in Ordnung, auch wenn einige Threads laufen länger:

user> (let [a (atom 1)] 
     (sequentialize 
     (future (Thread/sleep 100) 
       (swap! a #(* 10 %))) 
     (future (Thread/sleep 200) 
       (swap! a #(+ 20 %))) 
     (future (swap! a #(- %)))) 
     @a) 
;;=> -30 
0

Manifold eine Möglichkeit bietet, zukünftige with specific executor zu schaffen. Es ist nicht Teil von clojure lib, aber es ist immer noch eine qualitativ hochwertige lib und wahrscheinlich die beste Option für den Fall, dass Sie mehr Flexibilität brauchen, um mit Futures umzugehen, als Core lib bietet (ohne auf Java interop zurückgreifen zu müssen).

0

Zusätzlich die promises erwähnt, können Sie eine delay verwenden. Versprechen haben das Problem, dass Sie sie aus Versehen nicht liefern können, und erstellen ein Deadlock-Szenario, das mit future s und delay s nicht möglich ist. Der Unterschied zwischen einer Zukunft und einer Verzögerung ist nur der Thread, auf dem die Arbeit ausgeführt wird. Mit einer Zukunft wird die Arbeit im Hintergrund ausgeführt, und mit einer Verzögerung wird die Arbeit von dem ersten Thread ausgeführt, der versucht, es zu entfernen. Also, wenn Zukunft eine bessere Passform als Versprechen sind, könnten Sie immer so etwas wie:

(def result-1 (delay (long-calculation-1))) 
(def result-2 (delay (long-calculation-2))) 
(def result-3 (delay (long-calculation-3))) 

(defn run-calcs [] 
    @(future 
    @result-1 
    @result-2 
    @result-3)) 
Verwandte Themen