这可能是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()执行,一旦完成执行......但是再一次,这只是一个猜测.
正如您正确指出的那样,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)
| 归档时间: |
|
| 查看次数: |
278 次 |
| 最近记录: |