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?)

+3


source to share


1 answer


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 .

+4


source







All Articles