2013-07-12 21 views
6

Tôi có hai chức năng khác nhau trên hai tập dữ liệu rất lớn cần phải được xử lý, cuối cùng đến hai giá trị Boolean. các giá trị đó cần phải được trộn với nhau để có kết quả cuối cùng. câu hỏi của tôi là cách tốt nhất để tạo chủ đề để hai hàm dài có thể chạy cùng một lúc là gì. suy nghĩ của tôi là một cái gì đó như thế,với Clojure luồng quá trình chạy dài và so sánh lợi nhuận của họ

(def f (future longProcessOne(data_one))) 
(def g (future longProcessTwo(data_two))) 
(and @f @g) 

nhưng tôi đang tìm kiếm đầu vào về cách tốt hơn để thực hiện việc này.

+3

Liệu cách tiếp cận mà không hoạt động? Bạn có một vấn đề cụ thể với nó? Nó có vẻ khá tốt với tôi :) –

+0

trong khi nó làm việc, nó đã phải hoàn thành cả hai mà không thiếu nếu một thất bại. Tôi đang tìm cách làm điều tương tự nhanh hơn. – ChadJPetersen

+0

@ChadJPetersen Không, nó không phải hoàn thành cả hai; 'và' mạch ngắn. Nó không _cancel_ nó không sử dụng, nhưng đó là một mối quan tâm riêng biệt (và bạn _could_ hủy bỏ nó, nếu bạn muốn). –

Trả lời

3

(cách tiếp cận dựa trên Promise ở phía trên cùng, core.async dựa trên cách tiếp cận thấp xuống. Cả hai ngắn mạch trên giá trị falsey đầu tiên.)


Đây là một phiên bản sử dụng thực tế là một lời hứa duy nhất có thể được gửi nhiều lần (mặc dù chỉ giao hàng đầu tiên sẽ thành công trong việc thiết lập giá trị của nó); giao hàng tiếp theo chỉ đơn giản là trả lại nil mà không có tác dụng phụ).

(defn thread-and 
    "Computes logical conjunction of return values of fs, each of which 
    is called in a future. Short-circuits (cancelling the remaining 
    futures) on first falsey value." 
    [& fs] 
    (let [done (promise) 
     ret (atom true) 
     fps (promise)] 
    (deliver fps (doall (for [f fs] 
          (let [p (promise)] 
          [(future 
           (if-not (swap! ret #(and %1 %2) (f)) 
           (deliver done true)) 
           (locking fps 
           (deliver p true) 
           (when (every? realized? (map peek @fps)) 
            (deliver done true)))) 
          p])))) 
    @done 
    (doseq [[fut] @fps] 
     (future-cancel fut)) 
    @ret)) 

Một số xét nghiệm:

(thread-and (constantly true) (constantly true)) 
;;= true 

(thread-and (constantly true) (constantly false)) 
;;= false 

(every? false? 
     (repeatedly 100000 
        #(thread-and (constantly true) (constantly false)))) 
;;= true 

;; prints :foo, but not :bar 
(thread-and #(do (Thread/sleep 1000) (println :foo)) 
      #(do (Thread/sleep 3000) (println :bar))) 

Đưa ý tưởng của Arthur và A. Webb với nhau, bạn có thể sử dụng core.async tới và kết quả cùng thời gian ngắn mạch trên giá trị falsey đầu tiên trở lại :

(defn thread-and 
    "Call each of the fs on a separate thread. Return logical 
    conjunction of the results. Short-circuit (and cancel the calls 
    to remaining fs) on first falsey value returned." 
    [& fs] 
    (let [futs-and-cs 
     (doall (for [f fs] 
       (let [c (chan)] 
        [(future (>!! c (f))) c])))] 
    (loop [futs-and-cs futs-and-cs] 
     (if (seq futs-and-cs) 
     (let [[result c] (alts!! (map peek futs-and-cs))] 
      (if result 
      (recur (remove #(identical? (peek %) c) 
          futs-and-cs)) 
      (do (doseq [fut (map first futs-and-cs)] 
        (future-cancel fut)) 
       false))) 
     true)))) 

Kiểm tra với (constantly false)(constantly true):

(thread-and (constantly true) (constantly true)) 
;= true 
(thread-and (constantly true) (constantly false)) 
;= false 

;;; etc. 

Cũng lưu ý rằng ngắn mạch không thực sự làm việc:

;;; prints :foo before returning false 
(thread-and #(do (Thread/sleep 3000) false) 
      #(do (Thread/sleep 1000) (println :foo))) 

;;; does not print :foo 
(thread-and #(do (Thread/sleep 3000) false) 
      #(do (Thread/sleep 7000) (println :foo))) 
+0

Cũng có sẵn trong [gist] này (https://gist.github.com/michalmarczyk/5988137). –

+0

Có thể thực hiện điều này mà không có core.async không? –

+0

@event_jr: Trên thực tế, chỉ cần chỉnh sửa câu trả lời để bao gồm một phương pháp tiếp cận dựa trên lời hứa. Cũng có sẵn trong [gist] này (https://gist.github.com/michalmarczyk/5991353). –

5

Cách tiếp cận của bạn là mã Clojure khá bình thường. Một lựa chọn khác là sử dụng những lời hứa hoặc nếu bạn cần xử lý phức tạp hơn bạn có thể xem xét sử dụng một cái gì đó giống như lamina hoặc nếu bạn cảm thấy như sống trên chảy máu cạnh you could try core.async:

(ns async-example.core 
    (:require [clojure.core.async :refer :all]) 

(defn example [] 
    (let [a (chan) ; a channel for a to report it's answer 
     b (chan) ; a channel for b to report it's answer 
     output (chan)] ; a channel for the reporter to report back to the repl 
    (go (<! (timeout (rand-int 1000))) ; process a 
     (>! a (rand-nth [true false]))) 
    (go (<! (timeout (rand-int 1000))) ; process b 
     (>! b (rand-nth [true false]))) 
    (go (>! output (and (<! a) (<! b)))) ; the reporter process 
    output)) ;return the channe that the result will be sent to 

async-example.core> (<!! (go (<! (example)))) 
false 
async-example.core> (<!! (go (<! (example)))) 
false 
async-example.core> (<!! (go (<! (example)))) 
true 

Tất nhiên điều này là quá mức cần thiết cho tình hình của bạn, mặc dù đó là rất nhiều niềm vui anyway ;-)

+0

Sử dụng 'Chủ đề/ngủ' trong mã core.async có phần xấu xa - nó ăn hết hồ bơi thread. Cách core.async gốc để thực hiện điều này là với 'core.async/timeout', công cụ này sẽ đỗ trạng thái của khối trong khoảng thời gian. –

+0

có, điều đó hoàn toàn chính xác, tôi đã thay đổi mã của mình để sử dụng hết thời gian chờ. Cảm ơn bạn đã chỉ ra rằng –

Các vấn đề liên quan