2011-12-12 65 views
6

這可能是F#中最基本的東西之一,但我剛剛意識到我不知道在sceens背後發生了什麼。等待沒有阻塞線程? - 怎麼樣?

let testMe() = 
    async { printfn "before!" 
      do! myAsyncFunction() // Waits without blocking thread 
      printfn "after!" } 

testMe |> Async.RunSynchronously 

發生了什麼事do! myAsyncFunction()?我知道它在等待myAsyncFunction完成,然後再繼續。但是,它怎麼能這樣做,而不會阻塞線程?

我最好的猜測是,do! myAsyncFunction()後,一切都沿着一個延續,它獲取相同的線程myAsyncFunction()原定上,一旦myAsyncFunction()已完成執行上執行過..但話又說回來,這只是一個猜測。

+1

你的猜測是正確的,但我認爲將繼續對從線程池,不一定是相同的線程中的下一個可用的線程中運行。 – Daniel

回答

5

正如您已經正確指出的那樣,myAsyncFunction被傳遞了一個延續,它會調用它以在異步工作流完成時恢復其餘的異步工作流。

您可以更好地看代碼的脫糖版本的理解:

let testMe() = 
    async.Delay(fun() -> 
    printfn "before!" 
    async.Bind(myAsyncFunction(), fun() -> 
     printfn "after!" 
     async.Zero())) 

他們關鍵的一點是,通過myAsyncFunction創建異步流程是考慮到啓動它,並給它的Bind操作第二個參數(延續)作爲函數在工作流程完成時調用。如果你簡化了很多,然後異步工作流可以定義是這樣的:

type MyAsync<'T> = (('T -> unit) * (exn -> unit)) -> unit 

因此,異步工作流程只是需要一些延續作爲參數的函數。當它得到延續時,它會執行某些操作(即創建一個計時器或啓動I/O),然後它最終調用這些延續。問題「在哪個線索被稱爲?」是一個有趣的 - 在一個簡單的模型中,它取決於你開始的MyAsync - 它可能決定在想要的任何地方運行它們(即Async.SwithcToNewThread在新線程上運行它們)。 F#庫包含一些額外的處理,使得使用工作流程進行GUI編程變得更容易。

您的示例使用Async.RunImmediate來阻止當前線程,但您也可以使用Async.Start,它只是啓動工作流並在生成時忽略結果。的Async.Start實施看起來是這樣的:

let Start (async:MyAsync<unit>) = async (ignore, ignore)