यहां कई अच्छे उत्तरों हैं लेकिन मैंने सोचा कि मैं प्रश्न के लिए एक अलग कोण लेता हूं: एफ # एसिंक वास्तव में कैसे काम करता है?
सी # एफ # डेवलपर्स में async/await
के विपरीत वास्तव में Async
का अपना संस्करण लागू कर सकते हैं। Async
कैसे काम करता है यह जानने का यह एक शानदार तरीका हो सकता है।
(Async
दिलचस्पी स्रोत कोड के लिए यहां पाया जा सकता: https://github.com/Microsoft/visualfsharp/blob/fsharp4/src/fsharp/FSharp.Core/control.fs)
हमारे DIY workflows के लिए
हमारे मौलिक निर्माण खंड के रूप में हम परिभाषित:
type DIY<'T> = ('T->unit)->unit
यह एक समारोह है कि एक और स्वीकार करता है फ़ंक्शन (निरंतरता कहा जाता है) जिसे तब कहा जाता है जब 'T
का परिणाम तैयार है। यह कॉलिंग थ्रेड को अवरुद्ध किए बिना पृष्ठभूमि कार्य शुरू करने के लिए DIY<'T>
की अनुमति देता है। जब परिणाम तैयार होता है तो निरंतरता को गणना जारी रखने की अनुमति कहा जाता है।
एफ # Async
बिल्डिंग ब्लॉक थोड़ा अधिक जटिल है क्योंकि इसमें रद्दीकरण और अपवाद निरंतरता भी शामिल है लेकिन अनिवार्य रूप से यह है।
एफ # वर्कफ़्लो वाक्यविन्यास का समर्थन करने के लिए हमें एक गणना अभिव्यक्ति (https://msdn.microsoft.com/en-us/library/dd233182.aspx) को परिभाषित करने की आवश्यकता है। हालांकि यह एक उन्नत एफ # सुविधा है, यह एफ # की सबसे आश्चर्यजनक विशेषताओं में से एक है। परिभाषित करने के लिए दो सबसे महत्वपूर्ण संचालन return
& bind
जिनका उपयोग DIY<_>
बिल्डिंग ब्लॉक को DIY<_>
बिल्डिंग ब्लॉक में जोड़ने के लिए F # द्वारा किया जाता है।
adaptTask
Task<'T>
को DIY<'T>
में अनुकूलित करने के लिए उपयोग किया जाता है। startChild
कई simulatenous DIY<'T>
शुरू करने की अनुमति देता है, ध्यान दें कि ऐसा करने के लिए यह नए धागे शुरू नहीं करता है लेकिन कॉलिंग थ्रेड का पुन: उपयोग करता है।
Starting workflow
Waiting for key
Result is: (706L, 1, 2, 3)
कार्यक्रम ध्यान दें कि Waiting for key
के रूप में तुरंत छपा है चलाते समय:
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
कार्यक्रम के उत्पादन में कुछ इस तरह होना चाहिए:
किसी भी आगे की हलचल के बिना यहाँ नमूना कार्यक्रम है वर्कफ़्लो प्रारंभ करने से कंसोल थ्रेड अवरुद्ध नहीं है। लगभग 700ms के बाद परिणाम मुद्रित किया जाता है।
मुझे आशा है कि यह कुछ एफ # devs
http://stackoverflow.com/questions/2444676/understanding-f-asynchronous-programming –