我从专家f#,
一书中读到了以下代码为什么函数collectLinks
会在let! html = async { .... }
块中嵌入async
?如何通过删除内部异步来平放它?
waitForUrl
中urlCollector
中的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))
的问题。怎么样呢?
如何将实现与使用块队列实现的实现进行比较? https://msdn.microsoft.com/en-us/library/vstudio/hh297096(v=vs.100).aspx使用5创建一个块队列,并将该链接排入队列。
代码:
pscp
答案 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
的异步块返回,但我不认为这适用于此。