13.1.2 异步下载网页
来源:互联网 发布:体现中国实力的数据 编辑:程序博客网 时间:2024/06/05 14:37
13.1.2 异步下载网页
使用异步工作流抓取网页内容,需要引用 FSharp.PowerPack.dll库,它提供了许多 .NET方法的异步版本。开发独立的应用程序时,可以使用添加引用命令;在这一章,我们将使用交互式开发模式,因此,创建新的 F#脚本文件,使用 #r指令(清单 13.1)。
清单13.1使用异步工作流写代码(交互式F#)
> #r "FSharp.PowerPack.dll";;
> open System.IO
open System.Net;;
> let downloadUrl(url:string) = async { [1]
let request =HttpWebRequest.Create(url)
let! response = request.AsyncGetResponse() [2]
use response =response [3]
let stream =response.GetResponseStream()
use reader = newStreamReader(stream)
return!reader.AsyncReadToEnd() };; [4]
val downloadUrl : string -> Async<string>
导入(opening)所有必需的命名空间以后,就定义函数,实现异步工作流程了。它使用 async值作为计算生成器[1]。可以轻松地证明,它就是普通的值;如果使用 Visual Studio,在值的后面键入一个点(.),智能感知会显示计算生成器所包含的所有常用成员,比如,Bind和 Return,以及其他几个基本操作,在后面会要用到。输出的类型签名表明,计算类型是 Async<string>。后面我们会详细讨论这个类型。
在清单 13.1中,代码使用的 let!结构,执行了由 F#库提供的异步基本操作 AsyncGetResponse[2]。这个方法返回Async<WebResponse>类型,因此,let!结构组合了两个异步操作,把实际的 WebResponse值绑定到符号 response上。这样,异步操作完成后,我们就可以使用这个值了。
在下一行[3],使用到了use基本操作,对象一旦超出作用域,就会被释放。我们已经讨论过,在普通 F#程序中 use的用法,异步工作流中的 use非常类似,当工作流完成时,会立即释放 HTTP响应。我们使用值隐藏(value hiding),声明一个将被释放新值,隐藏原来的 response符号。这是一种常用模式,因此,F#提供了更简洁的语法,use!基本操作,把 let!和 use组合到一起。那么,上面的两行就可以替换成一行:
use! response = request.AsyncGetResponse()
清单 13.1的最后一行,我们使用了之前从没见过的基本操作 return![4],它能够运行其他的异步操作(就像使用 let!基本操作一样),只是当操作完成时,会返回结果,而不是绑定到符号。与 do!基本操作一样,它也也是简单的语法糖(syntactic sugar)。计算生成器不必实现任何其他的成员,编译器就可以把代码看作是这样写的(实际的转换更简单):
let! text = reader.AsyncReadToEnd()
return text
函数downloadUrl创建了异步计算之后,还应该确定如何用它来下载网页的内容。在清单 13.2中,我们使用了 Async模块中的函数来运行工作流。
清单13.2异步计算 (F# Interactive)
> let downloadTask = downloadUrl("http://www.manning.com/") ;; [1] <-- 生成异步工作流
val downloadTask : Async<string>
> Async.RunSynchronously(downloadTask);; [2]<--运行工作流,等待结果
val it : string = "<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0
Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html><head> (...)"
> let tasks =
[ downloadUrl("http://www/tomasp.net");
downloadUrl("http://www.manning.com") ];;
val tasks : list<Async<string>>
> let all = Async.Parallel(tasks);; [3]<--把几个工作流组合起来
val all : Async<string[]>
> Async.RunSynchronously(all);;
val it : string[] = [ "...";"..." ]
使用异步工作流写的代码是自动延迟的,因此,当我们执行第一行的 downloadUrl函数,它并不会开始下载网页[1]。返回值(Async<string>类型)表示期望运行的计算,就像函数值表示延迟运行的代码一样。Async模块提供了运行工作流的方法,表 13.1是其中一部分。
表 13.1在标准 F#库的 Async模块中,处理异步工作流的基本操作
基本操作
基本操作的类型和描述
RunSynchronously
Async<'T> –> 'T
在当前线程中启动给定的工作流。异步操作在工作流中使用时,工作流重新开始线程,用于调用异步回调。此操作会阻塞调用者线程,并等待工作流的结果。
Start
Async<unit> –> unit
在后台(使用线程池线程)启动给定的工作流,并立即返回。工作流与随后的调用者代码并行执行。从签名可知,工作流不返回值。
CreateAsTask
Async<'T> -> Task<'T>
这个方法仅在 .NET 4.0可用。它把异步工作流包装成可用于执行的 Task<'T>对象。任务可以用 Start或 RunSynchronously方法启动,其行为类似于 Async基本操作。使用Result属性,可以得到工作流的结果,如果工作流尚未完成,就会阻塞。
Parallel
seq<Async<'a>> -> Async<array<'a>>
得到异步工作流的集合,返回一个工作流,以并行方式执行所有参数值。返回的工作流等待所有操作完成,然后,在一个数组中返回结果。
在清单 13.2中,我们最初使用 Async.RunSynchronously[2],阻塞了调用线程,这对于以交互方式测试工作流,非常有用。在下一步,我们创建工作流值的列表,但在这里并不启动。有了集合以后,我们就可以使用 Async.Parallel 方法[3]生成一个工作流,并行执行列表中所有工作流。这仍然不会执行任何原始的工作流;只有再使用 Async.RunSynchronously,才启动组合的工作流,并等待结果。组合的工作流启动所有工作流并等待,直到所有的工作流都完成为止。
在等待最终结果期间,代码会阻塞,但运行的效率高了。它使用 .NET线程池来利用运行线程的最大值。如果我们创建了几百个任务,它并不会创建几百个线程,因为这样做的效率并不高,相反,应该使用少量的线程。当工作流遇到使用 let!结构,进行异步基本操作的调用时,会在系统中注册一个回调,并释放这个线程。因为 .NET使用线程池管理线程,完成任务的线程可以被重用,启动其他的异步工作流。当我们使用异步工作流时,并行运行的任务数可以远远大于直接使用的线程数。
在本章,我们以交互方式获取数据,因此,重点在并行运行工作流,并不关注响应灵敏的图形界面。后一类应用(也称为响应式应用(reactive applications))也很重要,我们将在第十六章讨论。现在,我们已经看到了使用异步工作流的代码,下面就看看它们是如何实现的。
- 13.1.2 异步下载网页
- 13.1.2 异步下载网页
- 13.1.2 异步下载网页
- 使用TcpClient 异步下载网页
- 异步下载
- 异步下载
- 下载网页
- 下载网页
- 网页异步加载
- 网页信息搜集助手 v1.2 下载
- python下载网页图片(2)
- 同步下载和异步下载
- 同步下载和异步下载
- 同步下载与异步下载
- 异步下载文件
- 简单实现异步下载
- (VB)FTP异步下载
- ASIHTTPRequest 异步下载
- Java 获取文件的基本信息
- 13.1.1 异步工作流为什么重要?
- Unreal里的SetLifeSpan()和InitialLifeSpan
- RESTful最佳实践——三、REST快速提示
- CA自认证和双向认证证书生成方法以及Nginx配置
- 13.1.2 异步下载网页
- linux下安装mysql5.7.17及简单配置
- 被工作逼到学习Windows编程的地步
- AngularJS实现轮播图
- 小博老师解析Java核心技术 ——JDBC普通查询操作
- C++输出格式控制
- 数据的统计量【均值、中位数、众数】
- 工作中浏览到的常用优秀播客
- Linked List