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

时间:2011-12-12 16:08:27

标签: multithreading f#

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

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

testMe |> Async.RunSynchronously

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

我最好的猜测是,do! myAsyncFunction()之后的所有内容都作为延续传递,在myAsyncFunction()完成执行后,会在同一个线程myAsyncFunction()上执行... {再说一遍,这只是猜测。

1 个答案:

答案 0 :(得分:5)

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

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

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

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

type MyAsync<'T> = (('T -> unit) * (exn -> unit)) -> unit

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

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

let Start (async:MyAsync<unit>) = async (ignore, ignore)