Home > Software design >  Does `Async.StartImmediate` run the `async` process on the same thread throughout the lifetime of th
Does `Async.StartImmediate` run the `async` process on the same thread throughout the lifetime of th

Time:11-07

This is in reference to F#'s Async.StartImmediate method. Possibly a diversion, but this method is confusingly named because Async.Start also starts the async process immediately, just on a thread pool.

Anyway, the documentation states that Async.StartImmediate starts the process using the calling thread. Does the async process continue to execute on that same thread throughout the lifetime of the process? Or is it possible it switches at some point? To my knowledge, Async.Start allows the process to switch underlying threads since it runs on top of a thread pool.

Edit: To clarify the question, I am thinking about an async that doesn't contain any other usage of async, let!, do!, return!, etc. for example:

async { printfn "testing" }

CodePudding user response:

I did an experiment that seems to imply that Async.StartImmediate does indeed propagate down to contained async processes.

// async.fsx
let outerAsync = async {
    printfn "Outer async thread: %A" System.Threading.Thread.CurrentThread.Name
    do! async {
        printfn "Inner async thread: %A" System.Threading.Thread.CurrentThread.Name
    }
}

System.Threading.Thread.CurrentThread.Name <- "main thread"
printfn "Main thread: %A" System.Threading.Thread.CurrentThread.Name

printfn "Async.StartImmediate"
Async.StartImmediate outerAsync

printfn "Async.Start"
Async.Start outerAsync

Then running this:

PS > dotnet fsi .\async.fsx
Main thread: "main thread"
Async.StartImmediate
Outer async thread: "main thread"
Inner async thread: "main thread"
Async.Start
Outer async thread: ".NET ThreadPool Worker"
Inner async thread: ".NET ThreadPool Worker"

Regarding if the computation stays on the thread, I found this in Expert F# 4.0 for Async.StartImmediate:

Starts the async computation on the current thread. It will run on the current thread until the first point where a continuation is scheduled for the thread; for example, at a primitive asynchronous I/O operation.

It further says:

This starts an async computation using the current thread to run the prefix of the computation. For example, if you start an async computation from a GUI thread, the prefix of the computation will run on the GUI thread.

I am not entirely sure what exactly "until the first point where a continuation is scheduled for the thread" or "the prefix of the computation" mean in terms of knowing when the async process will jump from the current thread, but this is the most information I could find.

CodePudding user response:

As you already found, the difference is that StartImmediate runs the prefix on the current thread while Start switches immediately. To get an actual continuation, you need a real async operation, e.g. Async.Sleep. async { } by itself is not async but lets you use async. That is, the difference is visible with

open System.Threading

Thread.CurrentThread.Name <- "Main"

let computation s = async {
    printfn "async prefix %s: %s" s Thread.CurrentThread.Name
    do! Async.Sleep 1
    printfn "async continuation %s: %s" s Thread.CurrentThread.Name
}

computation "StartImmediate" |> Async.StartImmediate
computation "Start" |> Async.Start

which prints

async prefix StartImmediate: Main
async prefix Start: .NET ThreadPool Worker
async continuation StartImmediate: .NET ThreadPool Worker
async continuation Start: .NET ThreadPool Worker

Note that the two operations are interleaved now and the continuation is run on the thread pool in both cases.

  • Related