用于说明F#中的异步工作流的常见示例是并行检索多个网页.下面是一个这样的例子:http://en.wikibooks.org/wiki/F_Sharp_Programming/Async_Workflows这里显示的代码是为了以后链接发生变化:
open System.Text.RegularExpressions
open System.Net
let download url =
let webclient = new System.Net.WebClient()
webclient.DownloadString(url : string)
let extractLinks html = Regex.Matches(html, @"http://\S+")
let downloadAndExtractLinks url =
let links = (url |> download |> extractLinks)
url, links.Count
let urls =
[@"http://www.craigslist.com/";
@"http://www.msn.com/";
@"http://en.wikibooks.org/wiki/Main_Page";
@"http://www.wordpress.com/";
@"http://news.google.com/";]
let pmap f l =
seq { for a in l -> async { return f a } }
|> Async.Parallel
|> Async.Run
let testSynchronous() = List.map downloadAndExtractLinks urls
let testAsynchronous() = pmap downloadAndExtractLinks urls
let time msg f =
let stopwatch = System.Diagnostics.Stopwatch.StartNew()
let temp = f()
stopwatch.Stop()
printfn "(%f ms) %s: %A" stopwatch.Elapsed.TotalMilliseconds msg temp
let main() =
printfn "Start..."
time "Synchronous" testSynchronous
time "Asynchronous" testAsynchronous
printfn "Done."
main()
Run Code Online (Sandbox Code Playgroud)
我想知道的是如何处理全局状态的变化,例如网络连接丢失?有一种优雅的方式来做到这一点?
可以在进行Async.Parallel调用之前检查网络状态,但状态可能会在执行期间发生变化.假设想要做的是暂停执行,直到网络再次可用而不是失败,是否有一种功能性的方法可以做到这一点?
首先,该示例存在一个问题 - 它用于并行Async.Parallel
运行多个操作,但操作本身并未实现为异步,因此这不会避免阻塞线程池中过多的线程.
异步.为了使代码完全异步的,download
和downloadAndExtractLinks
功能应该是异步的了,所以,你可以使用AsyncDownloadString
的WebClient
:
let asyncDownload url = async {
let webclient = new System.Net.WebClient()
return! webclient.AsyncDownloadString(System.Uri(url : string)) }
let asyncDownloadAndExtractLinks url = async {
let! html = asyncDownload url
let links = extractLinks html
return url, links.Count }
let pmap f l =
seq { for a in l -> async { return! f a } }
|> Async.Parallel
|> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)
重试.现在,回答这个问题 - 没有内置的机制来处理网络故障等错误,因此您需要自己实现这个逻辑.什么是正确的方法取决于您的情况.一种常见的方法是重试操作一定次数,并且只有在不成功例如10次时才抛出异常.您可以将其编写为带有其他异步工作流的原语:
let rec asyncRetry times op = async {
try
return! op
with e ->
if times <= 1 then return (reraise e)
else return! asyncRetry (times - 1) op }
Run Code Online (Sandbox Code Playgroud)
然后,您可以更改主函数以构建重试下载10次的工作流:
let testAsynchronous() =
pmap (asyncRetry 10 downloadAndExtractLinks) urls
Run Code Online (Sandbox Code Playgroud)
共享状态.另一个问题是,Async.Parallel
只有在所有下载完成后才会返回(如果有一个有缺陷的网站,则必须等待).如果你想在回来时显示结果,你需要更复杂的东西.
一个很好的方法是使用F#代理 - 创建一个代理,用于存储到目前为止获得的结果,并且可以处理两条消息 - 一条添加新结果,另一条返回当前状态.然后,您可以启动多个异步任务,将结果发送给代理,并且在单独的异步工作流中,您可以使用轮询来检查当前状态(例如,更新用户界面).
我写了MSDN一系列关于代理商也2 篇为developerFusion那有的是用F#代理代码示例.