从F#中的streamReader异步操作数据

时间:2015-06-22 16:00:04

标签: asynchronous f# streamreader

Read large txt file multithreaded?的行上,我怀疑它是否相当于将每个线程传递给Seq的切片块以及它是否能安全地处理并列行为; StreamReader是线程安全的吗?

以下是我用于测试此代码的代码(欢迎任何有关使用模式的建议或评论:))

nthreads = 4    

let Data = seq {
        use sr = new System.IO.StreamReader (filePath)
        while not sr.EndOfStream do
            yield sr.ReadLine ()
        }

let length = (Data |> Seq.length)

let packSize = length / nthreads

let groups =
     [ for i in 0..(nthreads - 1) -> if i < nthreads - 1  then Data |> Seq.skip( packSize * i )
                                                                    |> Seq.take( packSize )
                                                          else Data |> Seq.skip( packSize * i ) ]

let f = some_complex_function_modifiying_data

seq{ for a in groups -> f a }
        |> Async.Parallel
        |> Async.RunSynchronously

1 个答案:

答案 0 :(得分:2)

您的Data值的类型为seq<string>,这意味着它很懒。这意味着当您执行一些访问它的计算时,延迟序列将创建一个StreamReader的新实例,并独立于其他计算读取数据。

当您向seq { .. }块添加一些打印时,您可以轻松看到这一点:

let Data = seq {
    printfn "reading"
    use sr = new System.IO.StreamReader (filePath)
    while not sr.EndOfStream do
        yield sr.ReadLine ()  }

因此,您的并行处理实际上很好。它将为每个并行线程创建一个新计算,因此永远不会共享StreamReader个实例。

另一个问题是,如果这实际上是一件有用的事情 - 从磁盘读取数据通常是瓶颈,因此在一个循环中执行操作可能会更快。即使这样做,使用Seq.length是获取长度的慢速方法(因为它需要读取整个文件),而skip也是如此。更好(但更复杂)的解决方案可能是使用流Seek