【问题标题】:Wait without blocking thread? - How?等待不阻塞线程? - 如何?
【发布时间】:2011-12-12 16:08:27
【问题描述】:

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

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

testMe |> Async.RunSynchronously

do! myAsyncFunction() 发生了什么?我知道它会等待myAsyncFunction 完成,然后再继续。但是它怎么能做到这一点,而不阻塞线程呢?

我最好的猜测是,do! myAsyncFunction() 之后的所有内容都作为延续传递,一旦myAsyncFunction() 完成执行,就会在同一线程上执行myAsyncFunction()。但话又说回来,这只是一个猜测。

【问题讨论】:

  • 你的猜测是对的,虽然我认为延续会在线程池中的下一个可用线程上运行,不一定是同一个线程。

标签: multithreading f#


【解决方案1】:

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

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

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 1970-01-01
    相关资源
    最近更新 更多