iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 24
0
Software Development

擁抱 Clojure系列 第 24

[第 24 天] 擁抱 Clojure:並行與併發(一)

並行與併發(一)

建構軟體設計有兩種方式:
一種是簡單明顯地沒有缺陷,另一種則是複雜到沒有明顯的缺陷。

— 東尼•霍爾

現代計算機系統走向多核,爲了運用多核心的能力,開始利用程式語言或作業系統提供的執行緒及處理序,將任務切割後同時處理。而不同任務間如果有共同的資源需要維護,增加了編寫程式人員的負擔。

一般的程式語言會使用鎖或是互斥器,避免不同程式同時存取相同資源,但是一旦使用不當便會發生死鎖或競爭條件等問題。而不同程式之間共享同一資源,更有可能造成兩邊資訊更新不一致。

Clojure 中的不變性 (Immutable) 與持久存在 (Persistent),讓資料結構一旦建立就無法再更改並保持一定的效能,降低開發者面對錯綜複雜更新的風險。而設計巧妙的狀態管理,更減輕了開發者漸強的偏頭痛。

先來看看 Clojure 如何支援並行 (Parallelism)。

並行

並行 (Parallelism) 指的是同時有不同的程式分別去做各自任務,任務完成之後,將結果彙整起來。如果運行的環境具備多核心的能力,則任務便可以在不同的核心上執行,完成的時間將會減少。

既然 Clojure 建基在 Java 之上,自然可以使用 Java 中的執行緒類別,又由於 Clojure 中的函式實作了 Callable 與 Runnable 介面,使用起來自是容易許多:

(.start
 (Thread.
  (fn []
    (Thread/sleep 3000)
    (println "Thread ends."))))
;; => nil
;; 等待三秒
;; => Thread ends.

Future

Clojure 提供了更簡便的方式讓你將函式置於執行緒中運行。使用 future 將欲完成的任務置於另一個執行緒中運行,呼叫 future 會返回 Future 物件並開始運行任務。

當任務完成後返回值則存放在 Future 物件之中,取得返回值則使用標的函式 deref 或小老鼠符號 @ 於 Future 物件,即可取得任務執行後的結果。如任務尚未完成欲取得返回值,則會等待其計算完畢:

(def f (future (Thread/sleep 10000) (println "done") 100))
@f ;; 若在十秒內,此行將會停住等待計算完畢
;; => 100

你可以使用 future-done? 檢查一個 Future 物件是否完成運行:

(def f (future (Thread/sleep 10000) (println "done") 100))
(future-done? f) ;; 十秒內運行
;; => false
;; 十秒後
(future-done? f)
;; => true

使用 future-cancel 則會將一個已經開始運行的 Future 物件終止,如果對一個已經被強制終止的 Future 物件取用標的 (deref),會拋出例外:

(def f (future (Thread/sleep 10000) (println "done") 100))
(future-cancel f)
;; => true
@f
;; => CancellationException

Promise

利用 promise 建立的 Promise 物件則是與呼叫者建立約定,結果計算完畢之後會將它發送給持有 Promise 物件者。使用 promise 建立 Promise 物件、使用 deliver 傳送結果給 Promise 物件:

(def answer (promise))
(future (Thread/sleep 10000) (deliver answer 42))
;; => #future[{:status :pending, :val nil} 0x2b9dc292]
@answer ;; 十秒內運行的話,此行會停住
;; => 42

與 Future 一樣,若 Promise 物件尚未接收到結果,取用標的 Promise 物件將會停住等待結果送到。你可以使用 realized? 於 Promise 物件上,來取得結果是否已送達:

(def p (promise))
(realized? p)
;; => false
(deliver p :done)
(realized? p)
;; => true

(未完待續)


上一篇
[第 23 天] 擁抱 Clojure:讀取器與詮釋資料(二)
下一篇
[第 25 天] 擁抱 Clojure:並行與併發(二)
系列文
擁抱 Clojure30

尚未有邦友留言

立即登入留言