【问题标题】:Reading/writing data with async异步读取/写入数据
【发布时间】:2013-01-11 00:25:42
【问题描述】:

我不认为我正在编写的这段小代码有任何实际应用,但我试图将我的头脑围绕 async 并似乎有点挣扎。假设我想从 Yahoo 提取历史股价数据,将所有数据保存到单个 csv 文件中,然后使用批量复制将其加载到 SQL Server 中。我不太担心将数据加载到 SQL Server 中,但我想知道如何将数据写入新的 csv 文件。可以/应该异步完成吗?

据我所知,在抓取历史数据时无法将代码作为流的一部分抓取,因此我正在抓取流并将其映射到一个新列表,并将代码附加到每个项目的前面.有时,当我运行测试时,我会得到一个没有代码的记录,并且会有一个包含多个代码的记录(例如,“MSFT,YHOO”)。

所以,我的问题是,我怎样才能将这些数据转储到单个 csv 文件中而不引起问题?其次,当我拆分数据时,我得到一个空的尾部项目。丢弃它的最佳方法是什么?

就像我说的,我不知道这有什么实际应用,但我正在努力学习,所以希望你能原谅我的无知。感谢您的帮助,我真的很感激。这是我所拥有的:

open System
open System.IO
open System.Web
open System.Net

let fromDate = new DateTime(2013, 1, 1)

let getTickers = 
    "MSFT" :: "YHOO" :: []

let getData (ticker : string) = 
    async {
        let url = System.String.Format("http://ichart.finance.yahoo.com/table.csv?s={0}&g=d&ignore=.csv&a={1}&b={2}&c={3}", ticker, fromDate.Month - 1, fromDate.Day, fromDate.Year)

        Console.WriteLine(url)

        let req = WebRequest.Create(url)
        let! rsp = req.AsyncGetResponse()
        use stream = rsp.GetResponseStream()
        use reader = new StreamReader(stream)

        let lines = 
            reader.ReadToEnd().Split('\n')
            |> Seq.skip 1 // skip header
            |> Seq.map (fun line-> (String.Format("{0}, {1}", ticker, line.ToString())))

        Seq.iter (fun x->printfn "%s" (x.ToString())) lines
        ()
    }

let z = 
    getTickers 
    |> List.map getData
    |> Async.Parallel
    |> Async.RunSynchronously

【问题讨论】:

  • 您可以将消息发布到代理,然后有一个线程从代理获取它们并写入 csv 文件
  • 这可能属于codereview,但您可能会发现这answer to a related question 很有帮助。如果您只想将数据加载到 SQL Server 中,则无需先将其写入文件。
  • @ovatsus - 我会调查的。知道任何好的例子吗?
  • @Daniel - 感谢您的帮助。我知道我不需要先写入文件。事实上,我什至可能根本不会将数据发送到数据库,但我想达到我有一个从多个流创建的文件的地步。您引用的答案实际上是我的另一个问题。我倾向于使用从雅虎提取数据作为学习工具,所以这种情况下的数据库主要是事后才想到的。
  • 是的,我想并行写入文件。那么,假设我要提取 1000 只股票的数据,我可以同时将 MSFT、YHOO 和其他许多股票写入文件吗?我也不确定我的 List.map 调用是否在正确的位置,因为我有时会得到愚蠢的结果。

标签: asynchronous f#


【解决方案1】:

IMO,这有点过头了,但希望它能证明你想知道的:

open System
open System.IO
open System.Net

let tickers = 
  [ 
    "MSFT"
    "YHOO"
  ]

let getData (writer: TextWriter) ticker =
  async {
    let url = sprintf "http://ichart.finance.yahoo.com/table.csv?s=%s&g=d&ignore=.csv" ticker
    let req = WebRequest.Create(url)
    let! resp = req.GetResponseAsync() |> Async.AwaitTask
    use stream = resp.GetResponseStream()
    use reader = new StreamReader(stream)
    let! lines = reader.ReadToEndAsync() |> Async.AwaitTask
    let lines = 
      lines.Split('\n')
        |> Seq.skip 1
        |> Seq.filter ((<>) "") //skip empty lines
    for line in lines do
      do! writer.WriteLineAsync(String.Format("{0}, {1}", ticker, line)).ContinueWith(ignore) |> Async.AwaitTask
  }

let writeAllToFile path =
  use writer = new StreamWriter(path=path)
  tickers
    |> Seq.map (getData writer)
    |> Async.Parallel
    |> Async.RunSynchronously
    |> ignore

writeAllToFile @"C:\quotes.csv"

【讨论】:

  • 这应该在运行时给出时髦的结果,尤其是当有更多的代码时。 StreamWriter 不是线程安全的。
  • 毫无疑问。如果他想避免这种情况,他将不得不同步写入或使用排队代理。
  • 我得到了一些奇怪的结果,但我正朝着正确的方向前进。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2016-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多