Confusion about async in F #
I'm experimenting a bit with F # and I've written a class that listens for incoming UDP packets, prints them, and keeps listening.
I have four different implementations that do all of this.
type UdpListener(endpoint:IPEndPoint) =
let client = new UdpClient(endpoint)
let endpoint = endpoint
let rec listenAsync1() =
async {
let! res = client.ReceiveAsync() |> Async.AwaitTask
res.Buffer |> printfn "%A"
return! listenAsync1()
}
let rec listenAsync2() =
async {
let res = client.Receive(ref endpoint)
res |> printfn "%A"
do! listenAsync2()
}
let rec listenAsync3() =
async {
let res = client.Receive(ref endpoint)
res |> printfn "%A"
listenAsync3() |> Async.Start
}
let rec listenAsync4() =
async {
while true do
let res = client.Receive(ref endpoint)
res |> printfn "%A"
}
member this.Start() =
listenAsync1() |> Async.Start
listenAsync1
tries to use expected, returned client.ReceiveAsync()
and re-listen using recursion. This approach feels most functional to me.
However, the async computation expression actually runs the code in a block async
on the TP thread, so is it really necessary to use client.ReceiveAsync()
?
listenAsync2
does the same thing listenAsync1
as using a blocking call on the TP thread.
listenAsync3
uses a slightly different way of recursive knife listening to the listener.
listenAsync4
uses a loop. He clearly states his intentions, but in reality he is not.
Is there a benefit to using task-based Async in F #? Seems overkill if completed inside an async computation expression like Task.Run(..)
in C #.
Which method (if any) is generally accepted as best practice, and why? (Perhaps they can be ranked?)
source to share
When you use blocking calls, you are occupying a thread. That is, the thread is "busy" with your call and cannot be allocated for other work.
When you wait for a job, on the other hand, you give up control entirely and the thread is free to do other things.
In practice, this difference will manifest itself in your application, which will not scale to a large number of threads. That is, if you make two calls at once, you have occupied two threads. Four calls - four threads. Etc. It could be argued that this sort defeats the whole idea of ββbeing "asynchronous".
On the other hand, if you are making multiple calls at the same time with pending tasks, your application may not consume any threads at all (during in-flight calls)!
Because of this, all three blocking versions are significantly inferior. Use the first one.
Update : You can also look at this answer .
source to share