异步工作流Asynchronous workflows

本文介绍 F # 中的支持异步执行计算,即,不阻止执行其他工作。This article describes support in F# for performing computations asynchronously, that is, without blocking execution of other work. 例如,异步计算可用于编写应用程序,这些应用程序具有在应用程序执行其他工作时对用户保持响应的 Ui。For example, asynchronous computations can be used to write applications that have UIs that remain responsive to users as the application performs other work.


F # 的 docs.microsoft.com API 参考未完成。The docs.microsoft.com API reference for F# is not complete. 如果遇到任何断开的链接,请参阅F # 核心库文档If you encounter any broken links, reference F# Core Library Documentation instead.


async { expression }


在前面的语法中,由表示的计算 expression 设置为异步运行,也就是说,在执行异步睡眠操作、i/o 和其他异步操作时,不会阻止当前计算线程。In the previous syntax, the computation represented by expression is set up to run asynchronously, that is, without blocking the current computation thread when asynchronous sleep operations, I/O, and other asynchronous operations are performed. 异步计算通常在后台线程上启动,而在当前线程上继续执行。Asynchronous computations are often started on a background thread while execution continues on the current thread. 表达式的类型为 Async<'T> ,其中 'T 是使用关键字时表达式返回的类型 returnThe type of the expression is Async<'T>, where 'T is the type returned by the expression when the return keyword is used. 此类表达式中的代码称为异步块异步块The code in such an expression is referred to as an asynchronous block, or async block.

异步编程的方式有多种, Async 类提供的方法支持多种方案。There are a variety of ways of programming asynchronously, and the Async class provides methods that support several scenarios. 一般方法是创建 Async 表示要异步运行的计算或计算的对象,然后使用其中一个触发函数启动这些计算。The general approach is to create Async objects that represent the computation or computations that you want to run asynchronously, and then start these computations by using one of the triggering functions. 各种触发函数提供不同的方法来运行异步计算,使用哪种方法取决于你是要使用当前线程、后台线程还是 .NET Framework 任务对象,以及在计算完成时是否应运行延续函数。The various triggering functions provide different ways of running asynchronous computations, and which one you use depends on whether you want to use the current thread, a background thread, or a .NET Framework task object, and whether there are continuation functions that should run when the computation finishes. 例如,若要在当前线程上启动异步计算,可以使用 Async.StartImmediateFor example, to start an asynchronous computation on the current thread, you can use Async.StartImmediate. 当从 UI 线程启动异步计算时,您不会阻止用于处理用户操作(如击键和鼠标活动)的主事件循环,因此您的应用程序将保持响应。When you start an asynchronous computation from the UI thread, you do not block the main event loop that processes user actions such as keystrokes and mouse activity, so your application remains responsive.

使用 let 进行异步绑定!Asynchronous Binding by Using let!

在异步工作流中,某些表达式和操作是同步的,而有些则是用于以异步方式返回结果的更长的计算。In an asynchronous workflow, some expressions and operations are synchronous, and some are longer computations that are designed to return a result asynchronously. 以异步方式调用方法,而不是使用普通 let 绑定 let!When you call a method asynchronously, instead of an ordinary let binding, you use let!. 的作用 let! 是在执行计算时允许在其他计算或线程上继续执行。The effect of let! is to enable execution to continue on other computations or threads as the computation is being performed. 绑定的右侧 let! 返回后,异步工作流的其余部分将继续执行。After the right side of the let! binding returns, the rest of the asynchronous workflow resumes execution.

下面的代码演示了和之间的差异 let let!The following code shows the difference between let and let!. 使用的代码行 let 只是将异步计算创建为一个对象,您可以在以后使用来运行该对象,例如 Async.StartImmediateAsync.RunSynchronouslyThe line of code that uses let just creates an asynchronous computation as an object that you can run later by using, for example, Async.StartImmediate or Async.RunSynchronously. 使用的代码行 let! 开始计算,然后在结果可用之前挂起线程,此时将继续执行。The line of code that uses let! starts the computation, and then the thread is suspended until the result is available, at which point execution continues.

// let just stores the result as an asynchronous operation.
let (result1 : Async<byte[]>) = stream.AsyncRead(bufferSize)
// let! completes the asynchronous operation and returns the data.
let! (result2 : byte[])  = stream.AsyncRead(bufferSize)

除了之外 let! ,还可以使用 use! 来执行异步绑定。In addition to let!, you can use use! to perform asynchronous bindings. 与之间的差异与和的差异 let! use! 相同 let useThe difference between let! and use! is the same as the difference between let and use. 对于 use! ,在当前范围结束时释放对象。For use!, the object is disposed of at the close of the current scope. 请注意,在当前版本的 F # 语言中,不 use! 允许将值初始化为 null,即使是这样 useNote that in the current release of the F# language, use! does not allow a value to be initialized to null, even though use does.

异步基元Asynchronous Primitives

执行单个异步任务并返回结果的方法称为异步基元,它们专用于与一起使用 let!A method that performs a single asynchronous task and returns the result is called an asynchronous primitive, and these are designed specifically for use with let!. F # 核心库中定义了多个异步基元。Several asynchronous primitives are defined in the F# core library. 本模块中定义了两种 Web 应用程序方法 Microsoft.FSharp.Control.WebExtensionsWebRequest.AsyncGetResponseWebClient.AsyncDownloadStringTwo such methods for Web applications are defined in the module Microsoft.FSharp.Control.WebExtensions: WebRequest.AsyncGetResponse and WebClient.AsyncDownloadString. 在给定 URL 的情况下,这两个基元都从网页中下载数据。Both primitives download data from a Web page, given a URL. AsyncGetResponse生成一个 System.Net.WebResponse 对象,并 AsyncDownloadString 生成一个字符串,该字符串表示网页的 HTML。AsyncGetResponse produces a System.Net.WebResponse object, and AsyncDownloadString produces a string that represents the HTML for a Web page.

模块中包含了用于异步 i/o 操作的几个基元 Microsoft.FSharp.Control.CommonExtensionsSeveral primitives for asynchronous I/O operations are included in the Microsoft.FSharp.Control.CommonExtensions module. 类的这些扩展方法 System.IO.StreamStream.AsyncReadStream.AsyncWriteThese extension methods of the System.IO.Stream class are Stream.AsyncRead and Stream.AsyncWrite.

还可以通过定义一个函数,该函数的完整体包含在异步块中来编写自己的异步基元。You can also write your own asynchronous primitives by defining a function whose complete body is enclosed in an async block.

若要在使用 F # 异步编程模型为其他异步模型设计的 .NET Framework 中使用异步方法,您需要创建一个返回 F # 对象的函数 AsyncTo use asynchronous methods in the .NET Framework that are designed for other asynchronous models with the F# asynchronous programming model, you create a function that returns an F# Async object. F # 库的功能使此操作变得简单。The F# library has functions that make this easy to do.

此处包含使用异步工作流的一个示例;对于异步类的方法,文档中还有许多其他内容。One example of using asynchronous workflows is included here; there are many others in the documentation for the methods of the Async class.

此示例演示如何使用异步工作流并行执行计算。This example shows how to use asynchronous workflows to perform computations in parallel.

在下面的代码示例中,函数将 fetchAsync 获取从 Web 请求返回的 HTML 文本。In the following code example, a function fetchAsync gets the HTML text returned from a Web request. fetchAsync函数包含一个异步代码块。The fetchAsync function contains an asynchronous block of code. 绑定到异步基元的结果时,在此示例中 AsyncDownloadString ,让我们!When a binding is made to the result of an asynchronous primitive, in this case AsyncDownloadString, let! 使用而不是 let。is used instead of let.

使用函数 Async.RunSynchronously 可执行异步操作,并等待其结果。You use the function Async.RunSynchronously to execute an asynchronous operation and wait for its result. 例如,你可以通过将 Async.Parallel 函数与函数结合使用来并行执行多个异步操作 Async.RunSynchronouslyAs an example, you can execute multiple asynchronous operations in parallel by using the Async.Parallel function together with the Async.RunSynchronously function. Async.Parallel函数获取对象的列表 Async ,为每个 Async 任务对象设置要并行运行的代码,并返回一个 Async 表示并行计算的对象。The Async.Parallel function takes a list of the Async objects, sets up the code for each Async task object to run in parallel, and returns an Async object that represents the parallel computation. 与单个操作一样,调用 Async.RunSynchronously 开始执行。Just as for a single operation, you call Async.RunSynchronously to start the execution.

runAll函数并行启动三个异步工作流,并等待它们全部完成。The runAll function launches three asynchronous workflows in parallel and waits until they have all completed.

open System.Net
open Microsoft.FSharp.Control.WebExtensions

let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
                "MSDN", "http://msdn.microsoft.com/"
                "Bing", "http://www.bing.com"

let fetchAsync(name, url:string) =
    async {
            let uri = new System.Uri(url)
            let webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            printfn "Read %d characters for %s" html.Length name
            | ex -> printfn "%s" (ex.Message);

let runAll() =
    |> Seq.map fetchAsync
    |> Async.Parallel
    |> Async.RunSynchronously
    |> ignore


另请参阅See also