【IT168资讯】F#目前还有些待字闺中的意思,不过随着大家对F#了解的加深。希望更多的程序员能运用好F#。在此之前,我们51CTO曾报道过《详解F#异步及并行模式中的并行CPU及I/O计算》
在本系列的第3部分中,我们会来探索F#中轻量级的,交互式的代理,以及与代理有关的一些模式,包括“隔离的内部状态”。(译注:由于原文较长,因此译文分为两段,目前是第一段,讲解了F#中异步代理的基本使用方式。)
第1部分描述了F#作为一个并行及异步语言,是如何支持轻量级的响应操作,并给出了CPU异步并行和I/O异步并行两种模式。
第2部分描述了如何从异步计算或后台计算单元中获得结果。
模式4:您的第一个代理
我们来观察您所创建的第一个异步代理:
这个代理不断地异步等待消息,并将它们打印出来。在这段代码中,每个消息都是一个字符串,且agent的类型是:
agent.Post "hello!"这便会打印出:
got message 'hello!'也可以这样发送多条消息:
agent.Post (sprintf "message %d" i)
这样便可以打印出10000条消息。
您可以认为每个代理对象都包含一个消息队列(或管道),并在消息到达时进行响应。一个委托一般都使用异步的循环等待来消息并进行处理。如在上面的例子中,代理使用while循环进行处理。
许多读者可能已经对代理颇为熟悉了。如Erlang,它便是基于代理设计的(在那里被称为进程)。而不久之前,一个基于.NET平台的实验性的孵化型语言,Axum,也注重了基于代理编程的重要性。Axum与F#中的代理设计相互影响,而其他包含轻量级线程的语言也强调了基于代理的组合与设计。
上面的例子一开始创建了一个类型的缩写:Agent,它代表了F#类库中基于内存的代理类型“MailboxProcessor”。如果您愿意的话也可以使用这个完整的名字,不过我更喜欢简单的命名。
您的第一批10万个代理
代理对象非常轻量,这是因为它基于F#的异步编程模型。例如,您可以在一个.NET进程中创建成百上千,甚至更多个代理。例如,我们来创建10万个简单的代理对象:
您可以这样向每个代理对象发送消息:
for agent in agents do
agent.Post "ping!每第1万个代理对象会在收到消息时打印信息。这个代理集合在处理消息时非常迅速,只要几秒钟时间。代理和内存中的消息处理非常快。
很显然,代理并不与.NET线程直接对应──您不可能在单个应用程序中创建10万的线程(在32位操作系统中,即便1000个线程也已经太多了)。相反,在代理等待消息时,它实际上只是表现为一个回调函数,一些对象分配,以及代理所引用的闭包等等。在收到消息之后,代理的工作会在一个线程池(默认便是.NET线程池)中分配并执行。
尽管需要10万个代理的情况并不多见,不过2000多个代理倒是很正常的。接下来我们便会看到这样一些例子。
高伸缩的Web服务器处理请求
在F#编程中,异步代理的思想其实是一种在多个环境中反复出现的设计模式。在F#中,我们经常使用“代理”这个词表示一种随时发生的,特别是通过循环,或是处理消息,或是产生结果的异步计算。
例如,在以后的文章中,我们会来关注如何使用F#构建伸缩性强的TCP或HTTP服务器应用程序,并将它们部署到EC2或是Windows Azure中去。这里我们打算用“股票服务器”作为例子,它接受TCP或HTTP连接,并向客户端返回一系列的股票信息。每个客户端会每隔一秒钟收到一条股票信息。这个服务最终会以单个URL或REST API的形式发布。
在实现时,我们为每个客户端请求分配一个异步代理(由于只是演示,我们在这里便不断地写入相同的AAPL股票信息):
open System.Net.Sockets /// serve up a stream of quotes let serveQuoteStream (client: TcpClient) = async { let stream = client.GetStream() while true do do! stream.AsyncWrite( "AAPL 200.38"B ) do! Async.Sleep 1000.0 // sleep one second} 每个代理会一直运行到客户端连接断开。因为代理非常轻量,因此这个股票服务能够在一台机器上支持数千个并发连接(如果使用云托管服务则会有更好的伸缩性)。而同一时刻会出现多少个代理对象则取决于客户端的数量。
上面的例子演示了使用F#进行网络编程是多么的方便──网络协议在此变成了基于异步代理的数据流读写。在以后的文章中我们会观察更多使用F#进行伸缩性强的TCP/HTTP编程。
代理与隔离状态(命令式)
F#代理编程的一个优秀的关键之处便是其隔离性。隔离性则意味着资源“归属”与某个特定的代理,而不会暴露给其他代理。因此,独立状态对并发的访问及数据竞争是一种良好的保护。