這裏有很多很好的答案,但我認爲我對這個問題採取了不同的角度:F#的異步真的如何工作?
與C#F#中的async/await
不同,開發人員實際上可以實現自己的版本Async
。這可以是瞭解Async
如何工作的好方法。
(對於感興趣的源代碼Async
可以在這裏找到:https://github.com/Microsoft/visualfsharp/blob/fsharp4/src/fsharp/FSharp.Core/control.fs)
由於我們的基本構建模塊爲我們的DIY工作流程,我們定義:
type DIY<'T> = ('T->unit)->unit
這是接受另一個函數函數(稱爲繼續),在'T
類型的結果準備就緒時調用。這允許DIY<'T>
在不阻塞調用線程的情況下啓動後臺任務。當結果準備好時,繼續被調用,允許計算繼續。
該F#Async
構建塊有點複雜,因爲它還包括取消和異常延續,但本質上就是這樣。
爲了支持F#工作流語法,我們需要定義一個計算表達式(https://msdn.microsoft.com/en-us/library/dd233182.aspx)。雖然這是一個相當先進的F#功能,但它也是F#最令人驚喜的功能之一。定義的兩個最重要的操作是return
& bind
,F#使用它們將我們的DIY<_>
構造塊組合到聚合的DIY<_>
構件中。
adaptTask
用於將Task<'T>
修改爲DIY<'T>
。 startChild
允許啓動幾個simulatenous DIY<'T>
,請注意,它不啓動新線程爲了這樣做,但重新使用調用線程。
沒有這裏的任何進一步的ADO的示例程序:
open System
open System.Diagnostics
open System.Threading
open System.Threading.Tasks
// Our Do It Yourself Async workflow is a function accepting a continuation ('T->unit).
// The continuation is called when the result of the workflow is ready.
// This may happen immediately or after awhile, the important thing is that
// we don't block the calling thread which may then continue executing useful code.
type DIY<'T> = ('T->unit)->unit
// In order to support let!, do! and so on we implement a computation expression.
// The two most important operations are returnValue/bind but delay is also generally
// good to implement.
module DIY =
// returnValue is called when devs uses return x in a workflow.
// returnValue passed v immediately to the continuation.
let returnValue (v : 'T) : DIY<'T> =
fun a ->
a v
// bind is called when devs uses let!/do! x in a workflow
// bind binds two DIY workflows together
let bind (t : DIY<'T>) (fu : 'T->DIY<'U>) : DIY<'U> =
fun a ->
let aa tv =
let u = fu tv
u a
t aa
let delay (ft : unit->DIY<'T>) : DIY<'T> =
fun a ->
let t = ft()
t a
// starts a DIY workflow as a subflow
// The way it works is that the workflow is executed
// which may be a delayed operation. But startChild
// should always complete immediately so in order to
// have something to return it returns a DIY workflow
// postProcess checks if the child has computed a value
// ie rv has some value and if we have computation ready
// to receive the value (rca has some value).
// If this is true invoke ca with v
let startChild (t : DIY<'T>) : DIY<DIY<'T>> =
fun a ->
let l = obj()
let rv = ref None
let rca = ref None
let postProcess() =
match !rv, !rca with
| Some v, Some ca ->
ca v
rv := None
rca := None
| _ , _ ->()
let receiver v =
lock l <| fun() ->
rv := Some v
postProcess()
t receiver
let child : DIY<'T> =
fun ca ->
lock l <| fun() ->
rca := Some ca
postProcess()
a child
let runWithContinuation (t : DIY<'T>) (f : 'T -> unit) : unit =
t f
// Adapts a task as a DIY workflow
let adaptTask (t : Task<'T>) : DIY<'T> =
fun a ->
let action = Action<Task<'T>> (fun t -> a t.Result)
ignore <| t.ContinueWith action
// Because C# generics doesn't allow Task<void> we need to have
// a special overload of for the unit Task.
let adaptUnitTask (t : Task) : DIY<unit> =
fun a ->
let action = Action<Task> (fun t -> a())
ignore <| t.ContinueWith action
type DIYBuilder() =
member x.Return(v) = returnValue v
member x.Bind(t,fu) = bind t fu
member x.Delay(ft) = delay ft
let diy = DIY.DIYBuilder()
open DIY
[<EntryPoint>]
let main argv =
let delay (ms : int) = adaptUnitTask <| Task.Delay ms
let delayedValue ms v =
diy {
do! delay ms
return v
}
let complete =
diy {
let sw = Stopwatch()
sw.Start()
// Since we are executing these tasks concurrently
// the time this takes should be roughly 700ms
let! cd1 = startChild <| delayedValue 100 1
let! cd2 = startChild <| delayedValue 300 2
let! cd3 = startChild <| delayedValue 700 3
let! d1 = cd1
let! d2 = cd2
let! d3 = cd3
sw.Stop()
return sw.ElapsedMilliseconds,d1,d2,d3
}
printfn "Starting workflow"
runWithContinuation complete (printfn "Result is: %A")
printfn "Waiting for key"
ignore <| Console.ReadKey()
0
程序的輸出應該是這樣的:
Starting workflow
Waiting for key
Result is: (706L, 1, 2, 3)
當運行程序說明Waiting for key
被immidiately打印爲控制檯線程不會阻止啓動工作流程。大約700ms後打印結果。
我希望這有趣的是,一些F#開發者
http://stackoverflow.com/questions/2444676/understanding-f-asynchronous-programming – 2010-09-07 17:17:58