技术开发 频道

.NET中的异步编程:使用F#简化异步编程

    【IT168 技术】不管是使用yield或借助第三方类库来简化异步编程,或多或少总是感觉不那么“正统”,有点hack的感觉。这种感觉在实验阶段倒还可以,要是用在产品中总有点担心,即使这些类库来自权威的第三方,我不知道大家有没有跟我同样的感觉。那么这个时候我们就会想,如果在语言中直接能提供这种机制该多好呢。

        系列文章回顾:.NET开发中的异步编程:第三方类库

                      .NET中的异步编程:为什么需要异步

                      .NET异步编程:IO完成端口与BeginRead

                      .NET异步编程:使用CPS及yield实现异步

                      .NET中的异步编程:传统的异步编程

  F#的异步工作流

  在Visual Studio 2010中,新包含了一种语言:F#。F#的一大特性就是异步计算。能让你用同步的方式编写异步的代码,不用使用AsyncCallback回调将一个方法分为两段,也不用注册异步完成事件。

  F#是一个强类型的函数式编程语言,现在是2.0版本,在VS2010中正式作为first-class语言出现。其主要设计者是Don Syme,同是.NET中的泛型的主要设计者之一。

  我们来看看前面几篇文章中都包含的那个示例使用F#的代码将是怎样:

       let asyncDownload (url:string) =

  async{

  
let req = WebRequest.Create(url)

  
let! resp = req.AsyncGetResponse()

  use stream
= resp.GetResponseStream()

  
let reader = new StreamReader(stream)

  return reader.ReadToEnd()

  }

  很短小精悍吧(实际上这段代码可以更短,但为了说明异步的编写方式,我没有使用那些看起来有点“怪”的语法)。下面我们来解读一下这段代码,希望本文结束后你能对F#中的异步有点初步的印象。

  F#中用let定义一个值,比如:

let value = 5

  不过上面的代码是用let定义一个函数。不过请记住,F#是函数式编程语言,在这里函数也是值。观察上面的代码,let后面是函数名,然后跟着函数的参数列表(url : string)。F#有强大的类型推断能力,一般情况下参数的类型是不需要写的,但是在这个代码中因为WebRequest.Create方法有多个重载方式,所以无法推断出url的类型,需要加上string。注意这里定义类型的方式。

  下面是本问最有趣的地方了:async{…}。这就是所谓的异步工作流,它实际上是AsyncBuilder的一个全局示例。而被包括在async{}内的一些操作被转换为FSharpAsyncBuilder的方法调用,这种使用方式在F#里称之为工作流(Workflow)。注意,这和业务系统中的工作流程系统是不同的。关于工作流我会在下一篇文章中详细介绍。现在我们只需要知道async{}执行的结果放回的是一个Async<’a>(泛型)类型的东西,这个东西表示一个可以被调度运行的任务,我们可以这样使用这个方法:

Async.RunSynchronously(asyncDownload("http://www.google.com"))

   Async.RunSynchronously会将asyncDownload返回的这个异步任务放到线程池中执行,然后阻塞线程等待返回结果。

  在这里需要说明的是,前面下载网页内容的代码是异步执行的。当调用req.AsyncGetResponse时只是注册异步回调,但是并不阻塞线程。当从远程服务器拿到响应后会接着执行req.AsyncGetResponse后面的内容。这样就用非常自然的同步顺序的方式,编写出异步的代码。这比之前使用各种第三方类库的hack方式看起来更自然。至于这种方式背后的实现原理,敬请期待下篇文章。

  但这里存在一个问题是,如果我们现在的场景是这样的:

  在Winform窗体上有一个按钮,点击按钮后下载网页内容,然后将内容显示在页面上的一个文本框中。那么这里使用Async.RunSynchronously就不是个好注意。RunSynchronously会阻塞UI线程直到返回结果,那么我们在本系列开始的时候提到构建灵敏的UI的目的就没有达到。我们不能阻塞UI线程,应该让它执行完毕后调用我们的代码将内容显示在文本框中。幸好Async提供了StartWithContinuations方法,我们就可以这样来执行我们的下载程序了:

 Async.StartWithContinuations(asyncDownload("http://www.google.com",

  (func html
-> textBox.Text = html),

  (func ex
-> MessageBox.Show(ex.Message))

  (func canl
-> MessageBox.Show("canceled"))

   Async.StartWithContinuations第一个参数接收的是我们异步执行的任务,然后接收三个回调。这三个回调就是我在.NET中的异步编程(三)- Continuation passing style以及使用yield实现异步中介绍的continuation。它们分别是成功执行后将怎样(在textBox中显示),发生异常后怎样(使用MessageBox显示警告),以及任务被取消了将怎样。使用这个后这里就不再有阻塞了,continuation是在asyncDownload执行完毕后触发的,不是通过阻塞线程“等”来的。

0
相关文章