Asynchronous 为什么嵌套异步计算?

Asynchronous 为什么嵌套异步计算?,asynchronous,f#,Asynchronous,F#,考虑以下两种方法来构造计算1+2的Async计算: let c1 = async { let a = 1 let b = 2 return a + b } let c2 = async { let a = 1 let! b = async { return 2 } return a + b } 它们之间的实际区别是什么?在我看来,他们做了同样的事情。例如,为什么您需要使用let!结

考虑以下两种方法来构造计算
1+2
Async
计算:

let c1 =
    async {
        let a = 1
        let b = 2
        return a + b }

let c2 =
    async {
        let a = 1
        let! b = async { return 2 }
        return a + b }

它们之间的实际区别是什么?在我看来,他们做了同样的事情。例如,为什么您需要使用
let!结果=streamReader.ReadToEndAsync()
而不是
让结果=streamReader.ReadToEnd()
?当计算运行时,不是两行都“阻塞”了吗?

你的
let愚蠢的例子!b=async{return 2}
确实没有带来任何新东西。它实际上完全等同于
让b=2

然而,
ReadToEnd
ReadToEndAsync
的情况不同。虽然它们都可以被描述为“阻塞”,但它们是以完全不同的方式阻塞的

ReadToEnd
是同步的。当在线程上调用它时,该线程停止并等待它完成。线程被阻塞了。它什么也不做,但也不能用来执行其他任何事情

ReadToEndAsync
使用(在Windows上也称为“”。这基本上意味着线程在这一点上停止并调用操作系统,说“嘿,请帮我读这个文件,完成后叫醒我”。它在较低级别的实现方式因操作系统而异,但通常在调用完成时会收到某种回调

这在高可用性高并发系统(如HTTP服务器)中非常重要。但是,如果您只是在人工监督下在计算机上本地运行脚本,那么只需使用更方便的方法即可


有趣的是,同步版本
ReadToEnd
实际上也在后台使用异步I/O,它只是包装在一个同步阻塞包装器中,以便在简单情况下更方便地使用。

let!b=async{return 2}
,右侧被发送到线程池进行求值?实际上,不是。所有操作都同步执行,直到出现实际的异步调用(即,不同步调用成功时的
OnSuccess
)的计算)。与普通的
async
-更少的程序(涉及蹦床)相比,肯定会有相当大的开销,但它肯定不会无缘无故地进行线程切换。