我理解f#和c#异步模型之间的主要区别在于f#中,除非你调用类似Async.RunSynchronously的东西,否则异步执行不会开始.在c#中,当方法返回任务时,通常(并非总是)立即在后台线程中启动执行.
Async.AwaitTask文档说"返回一个等待给定任务完成并返回其结果的异步计算."
这是否意味着,当您调用返回任务的ac#方法时,执行已经在后台启动了?如果是这样,那么将它包装在Async类型中又有什么意义呢?
rmu*_*unn 15
在Async中包装Task的目的是更容易地将其与其他asyncs组合,或者let!
在async { ... }
块内部使用它.在后一种情况下,包装的任务将在其封闭async { ... }
块启动之前不会启动.
例如,让我们看看以下函数:
let printTask str =
async {
printfn "%s" str
} |> Async.StartAsTask
Run Code Online (Sandbox Code Playgroud)
这没什么用; 它存在的唯一原因是你可以告诉它什么时候开始运行,因为它会在屏幕上打印一条消息.如果你从F#Interactive调用它:
printTask "Hello"
Run Code Online (Sandbox Code Playgroud)
您将看到以下输出:
Hello
val it : Threading.Tasks.Task<unit> =
System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit]
{AsyncState = null;
CreationOptions = None;
Exception = null;
Id = 4;
IsCanceled = false;
IsCompleted = true;
IsCompletedSuccessfully = true;
IsFaulted = false;
Status = RanToCompletion;}
Run Code Online (Sandbox Code Playgroud)
所以它打印"Hello"然后返回完成的Task.这证明该任务立即启动.
但现在看下面的代码:
open System.Net
open System
open System.IO
let printTask str =
async {
printfn "%s" str
} |> Async.StartAsTask
let fetchUrlAsync url =
async {
let req = WebRequest.Create(Uri(url))
do! printTask ("started downloading " + url) |> Async.AwaitTask
use! resp = req.GetResponseAsync() |> Async.AwaitTask
use stream = resp.GetResponseStream()
use reader = new IO.StreamReader(stream)
let html = reader.ReadToEnd()
do! printTask ("finished downloading " + url) |> Async.AwaitTask
}
Run Code Online (Sandbox Code Playgroud)
(这是Scott Wlaschin的"Async Web Downloader"示例,适用于在内部使用Tasks而不是Asyncs).
这里,该async { ... }
块包含三个任务,所有任务都包含在内Async.AwaitTask
.(请注意,如果|> Async.AwaitTask
从这些行中删除任何一行,则会出现类型错误).对于每个任务,一旦其代码行执行,它将立即开始.但这是一个重要的观点,因为整体async { ... }
计算并没有立即开始.所以我可以这样做:
let a = fetchUrlAsync "http://www.google.com"
Run Code Online (Sandbox Code Playgroud)
而F#Interactive中唯一印刷的是val a : Async<unit>
.我可以等到我想要的时间,也没有别的东西打印出来.只有我真正开始 a
它才会开始运行:
a |> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)
这会started downloading http://www.google.com
立即打印,然后在短暂停顿后打印finished downloading http://www.google.com
.
这样做的目的是Async.AwaitTask
:允许async { ... }
块更容易地与返回Tasks的C#代码互操作.而如果Async.AwaitTask调用内部async { ... }
的块,然后将任务才会真正开始直至关闭Async
开始,所以你仍然可以得到的"冷启动"异步操作的所有优点.