我正在尝试实现一个我从Don Syme的博客中读到的模式
这表明利用异步I/O有机会极大地提高性能。我目前正在尝试使用Array.Parallel.Map以某种方式“工作”一段代码,看看使用Async.Parallel是否能以某种方式获得相同的结果,但我真的不了解Async.Parallel,也不能让任何东西工作。
我有一段代码(为了说明这一点,在下面进行了简化),它可以成功地检索一个cusip的数据数组。(例如,价格系列)
let getStockData cusip =
let D = DataProvider()
let arr = D.GetPriceSeries(cusip)
return arr
let data = Array.Parallel.map (fun x -> getStockData x) stockCusips因此,这种方法通过互联网连接到我的数据供应商,为每只股票(可能多达3000只)构建一个数组数组,并返回一个数组数组(每只股票1个,每个股票有一个价格序列)。诚然,我不明白Array.Parallel.map背后发生了什么,但我想知道这是不是一种在幕后浪费资源的场景,实际上使用异步I/O会更快吗?因此,为了测试这一点,我尝试使用异步来实现此函数,我认为下面的函数遵循Don Syme文章中使用URL的模式,但它不会使用"let!“进行编译。
let getStockDataAsync cusip =
async { let D = DataProvider()
let! arr = D.GetData(cusip)
return arr
} 我得到的错误是:此表达式的类型应为Async<'a>,但这里的类型为obj
它使用"let“而不是"let!”可以很好地编译,但我认为整个要点是您需要感叹号才能使命令在不阻塞线程的情况下运行。
因此,第一个问题实际上是,我在上面的getStockDataAsync中的语法有什么问题,然后在更高的级别上,有人可以提供一些关于异步I/O的更多见解,以及我所介绍的方案是否会从中受益,使其潜在地比Array.Parallel.map快得多?非常感谢。
发布于 2010-03-19 23:09:55
F#异步工作流允许您实现异步计算,但是,F#对常规计算和异步计算进行了区分。这种差异是由类型系统跟踪的。例如,一个同步下载网页的方法的类型为string -> string (获取URL并返回超文本标记语言),而异步执行相同操作的方法的类型为string -> Async<string>。在async块中,可以使用let!调用异步操作,但所有其他(标准同步)方法都必须使用let调用。现在,您的示例的问题是GetData操作是普通的同步方法,所以您不能使用let!调用它。
在典型的F#场景中,如果希望使GetData成员异步,则需要使用异步工作流来实现它,因此还需要将其包装在async块中。在某些情况下,您将到达确实需要异步运行一些原始操作的位置(例如,从web站点下载数据)。F#提供了几个基本的异步操作,您可以使用let!从async块调用这些操作,比如AsyncGetResponse (它是GetResponse方法的异步版本)。因此,在您的GetData方法中,例如,您将编写如下内容:
let GetData (url:string) = async {
let req = WebRequest.Create(url)
let! rsp = req.AsyncGetResponse()
use stream = rsp.GetResponseStream()
use reader = new System.IO.StreamReader(stream)
let html = reader.AsyncReadToEnd()
return CalculateResult(html) }总结一下,您需要确定一些基本的异步操作(例如等待web服务器或文件系统),在这一点上使用基本的异步操作,并将使用这些操作的所有代码包装在async块中。如果没有可以异步运行的原语操作,那么您的代码是受CPU限制的,您可以只使用Parallel.map。
我希望这能帮助你理解F#异步工作流是如何工作的。要了解更多信息,您可以查看Don Syme's blog post、关于asynchronous programming by Robert Pickering的系列文章或my F# web cast。
发布于 2010-03-19 23:34:01
@Tomas已经有了一个很好的答案。我只会说几个额外的东西。
异步方法的习惯用法是使用“F#”前缀命名方法(AsyncFoo,而不是FooAsync;后者是另一种.NET技术已经使用的习惯用法)。所以你的函数应该是getStockData和asyncGetStockData。
在异步工作流中,每当您使用let!而不是let或do!而不是do时,右边的内容应该是Async<T>类型而不是T类型。基本上,您需要一个现有的异步计算,以便在工作流程中的这一点“进行异步”。每个Async<T>本身要么是某个其他的async{...}工作流,要么是一个异步“原语”。这些原语在F#库中定义,或者通过Async.FromBeginEnd或Async.FromContinuations在用户代码中创建,从而能够定义启动计算、注册I/O回调、释放线程以及在被回调时重新启动计算的低级细节。因此,为了获得异步I/O的全部好处,您必须一直“探测”异步,直到某些真正的异步I/O原语。
https://stackoverflow.com/questions/2477931
复制相似问题