等待没有阻塞线程?- 怎么样?

ebb*_*ebb 6 f# multithreading

这可能是F#中最基本的东西之一,但我刚刚意识到我不知道这些情况背后会发生什么.

let testMe() = 
    async { printfn "before!"
            do! myAsyncFunction() // Waits without blocking thread
            printfn "after!" }

testMe |> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)

发生了什么事do! myAsyncFunction()?我知道它等待myAsyncFunction完成,然后继续前进.但是如何在不阻塞线程的情况下做到这一点呢?

我最好的猜测是,后续的所有内容都do! myAsyncFunction()作为一个延续传递,它在同一个线程myAsyncFunction()myAsyncFunction()执行,一旦完成执行......但是再一次,这只是一个猜测.

Tom*_*cek 5

正如您正确指出的那样,myAsyncFunction传递了一个延续,并在完成时调用它以恢复异步工作流的其余部分.

通过查看代码的desugared版本,您可以更好地理解它:

let testMe() =  
  async.Delay(fun () ->  
    printfn "before!" 
    async.Bind(myAsyncFunction(), fun () ->
        printfn "after!" 
        async.Zero())) 
Run Code Online (Sandbox Code Playgroud)

它们的关键在于,创建的异步工作流myAsyncFunction被赋予Bind启动它的操作,并为其提供第二个参数(延续)作为在工作流完成时调用的函数.如果您简化了很多,那么可以像这样定义异步工作流:

type MyAsync<'T> = (('T -> unit) * (exn -> unit)) -> unit
Run Code Online (Sandbox Code Playgroud)

因此,异步工作流只是一个将一些延续作为参数的函数.当它获得延续时,它会执行某些操作(即创建计时器或启动I/O),然后最终调用这些延续.问题"在哪个线程上称为延续?" 是一个有趣的 - 在一个简单的模型中,它取决于MyAsync你正在开始 - 它可能决定在任何想要的地方运行它们(即Async.SwithcToNewThread在新线程上运行它们).F#库包含一些额外的处理,使使用工作流程的GUI编程更容易.

您的示例使用Async.RunImmediate,阻止当前线程,但您也可以使用Async.Start,它只是启动工作流并在生成时忽略结果.实现Async.Start可能如下所示:

let Start (async:MyAsync<unit>) = async (ignore, ignore)
Run Code Online (Sandbox Code Playgroud)