为什么在异步中嵌入异步?

时间:2015-06-16 04:20:29

标签: f#

我从专家f#,

一书中读到了以下代码
  1. 为什么函数collectLinks会在let! html = async { .... }块中嵌入async?如何通过删除内部异步来平放它?

  2. waitForUrlurlCollector中的do! Async.StartChild (async {....}) |> Async.Ignore函数在外async块中有open System.Collections.Generic open System.Net open System.IO open System.Threading open System.Text.RegularExpressions let limit = 50 let linkPat = "href=\s*\"[^\"h]*(http://[^&\"]*)\"" let getLinks (txt:string) = [ for m in Regex.Matches(txt,linkPat) -> m.Groups.Item(1).Value ] // A type that helps limit the number of active web requests type RequestGate(n:int) = let semaphore = new Semaphore(initialCount=n, maximumCount=n) member x.AsyncAcquire(?timeout) = async { let! ok = Async.AwaitWaitHandle(semaphore, ?millisecondsTimeout=timeout) if ok then return { new System.IDisposable with member x.Dispose() = semaphore.Release() |> ignore } else return! failwith "couldn't acquire a semaphore" } // Gate the number of active web requests let webRequestGate = RequestGate(5) // Fetch the URL, and post the results to the urlCollector. let collectLinks (url:string) = async { // An Async web request with a global gate let! html = async { // Acquire an entry in the webRequestGate. Release // it when 'holder' goes out of scope use! holder = webRequestGate.AsyncAcquire() let req = WebRequest.Create(url,Timeout=5) // Wait for the WebResponse use! response = req.AsyncGetResponse() // Get the response stream use reader = new StreamReader(response.GetResponseStream()) // Read the response stream (note: a synchronous read) return reader.ReadToEnd() } // Compute the links, synchronously let links = getLinks html // Report, synchronously do printfn "finished reading %s, got %d links" url (List.length links) // We're done return links } /// 'urlCollector' is a single agent that receives URLs as messages. It creates new /// asynchronous tasks that post messages back to this object. let urlCollector = MailboxProcessor.Start(fun self -> // This is the main state of the urlCollector let rec waitForUrl (visited : Set<string>) = async { // Check the limit if visited.Count < limit then // Wait for a URL... let! url = self.Receive() if not (visited.Contains(url)) then // Start off a new task for the new url. Each collects // links and posts them back to the urlCollector. do! Async.StartChild (async { let! links = collectLinks url for link in links do self.Post link }) |> Async.Ignore // Recurse into the waiting state return! waitForUrl(visited.Add(url)) } // This is the initial state. waitForUrl(Set.empty)) 的问题。怎么样呢?

  3. 如何将实现与使用块队列实现的实现进行比较? https://msdn.microsoft.com/en-us/library/vstudio/hh297096(v=vs.100).aspx使用5创建一个块队列,并将该链接排入队列。

  4. 代码:

    pscp

1 个答案:

答案 0 :(得分:6)

我可以想到async代码调用另一个异步块的一个原因,即它允许您在嵌套块完成时更早地处理资源。为了证明这一点,这里有一个帮助者在调用Dispose时打印一条消息:

let printOnDispose text = 
  { new System.IDisposable with
      member x.Dispose() = printfn "%s" text }

以下使用嵌套async在嵌套块中执行某些操作,然后清除嵌套块中使用的本地资源。然后它会睡一些,并清理外部块中使用的资源:

async { 
  use bye = printOnDispose "bye from outer block"
  let! r = async {
    use bye = printOnDispose "bye from nested block"
    do! Async.Sleep(1000)
    return 1 }
  do! Async.Sleep(1000) }
|> Async.Start

这里,“嵌套块”资源在1秒后被处理掉,外部块资源在2秒后被处理掉。

在其他情况下,嵌套async很有用(例如从包含try .. with的异步块返回,但我不认为这适用于此。