在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
答案 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
。