經過 昨天 的推導,我們的平行化 library 中定義了 Par[A]
容器型態,然後核心 API 中包含了以下 function,
def unit[A](a: A): Par[A]
def map2[A, B, C](a: Par[A], b: Par[B])(f: (A, B) => C): Par[C]
def fork[A](a: => Par[A]): Par[A]
def lazyUnit[A](a: => A): Par[A]
def run[A](a: Par[A]): A
我們也簡單定義了每個 function 的職責:
unit
使一般的值變成平行化計算。map2
使用 high-order function 把 2 個平行化計算的合併成 1 個平行化計算。fork
將某一個平行化計算從主執行緒 fork 出來,此計算不會馬上運行,直到被 run 觸發。lazyUnit
包裹一個還未計算的表達式成為平行化計算並 fork 它run
實際運行平行化計算並從中提取值run 需要真正的開始執行平行化計算了,我們或許可以自行實現底層 API,但是,Java 標準庫中已經有 java.util.concurrent.ExecutorService
可供我們使用,如果把它翻譯成 Scala 的話 API 定義如下:
trait ExecutorService:
def submit[A](a: Callable[A]): Future[A]
trait Callable[A]:
def call: A
trait Future[A]:
def get: A
def get(timeout: Long, unit: TimeUnit): A
def cancel(mayInterruptIfRunning: Boolean): Boolean
def isDone: Boolean
def isCancelled: Boolean
ExecutorService 需提交 Callable 然後回傳 Future,Future 隱含著會執行在不同執行緒上,然後透過會等待的 get 取得值,
我們嘗試讓 run 存取 ExecutorService,
def run[A](s: ExecutorService)(a: Par[A]): A
所以 Par[A] 可以想像成代表了 ExecutorService => A
,因為 A 代表了要從 Future 中 get,為了更好的推展延遲運行,以及用到 Future 那些 function,我們可以把 Par[A] 型態改成這樣 ExecutorService => Future[A]
,然後讓我們的 run 回傳 Future,
type Par[A] = ExecutorService => Future[A]
def run[A](s: ExecutorService)(a: Par[A]): Future[A] = a(s)
type 的意思可以參考 Day 13。
現在我們的 Par[A] 是 function ExecutorService => Future[A]
的別名,操作 Par 型態也不用擔心會立即建立新的執行緒,除非我們調用 run 並提供 ExecutorService,才會取得實際執行在不同執行緒上的 Future 物件。
明天繼續實作其他 function!