如何限制传递给Async.Parallel

时间:2015-06-17 00:26:02

标签: asynchronous f#

我有一个包含大量小异步数据库查询的数组;例如:

// I actually have a more complex function that
// accepts name/value pairs for query parameters.
let runSql connString sql = async {
    use connection = new SqlConnection(connString)
    use command = new SqlCommand(sql, connection)
    do! connection.OpenAsync() |> Async.AwaitIAsyncResult |> Async.Ignore
    return! command.ExecuteScalarAsync() |> Async.AwaitTask
    }

let getName (id:Guid) = async {
    // I actually use a parameterized query
    let querySql = "SELECT Name FROM Entities WHERE ID = '" + id.ToString() + "'"
    return! runSql connectionString querySql
    }

let ids : Guid array = getSixtyThousandIds()

let asyncWorkflows = ids |> Array.map getName
//...

现在,问题是:下一个表达式一次运行所有60K工作流,充斥着服务器。这导致许多SqlCommand超时;它通常会导致客户端内存异常(F#交互)由于我不理解的原因(并且不需要理解它们)没有进行调查:

//...
let names =
    asyncWorkflows
    |> Async.Parallel
    |> Async.RunSynchronously

我已经编写了一个粗略的函数来批量处理请求:

let batch batchSize asyncs = async {
    let batches = asyncs
                  |> Seq.mapi (fun i a -> i, a)
                  |> Seq.groupBy (fst >> fun n -> n / batchSize)
                  |> Seq.map (snd >> Seq.map snd)
                  |> Seq.map Async.Parallel
    let results = ref []
    for batch in batches do
        let! result = batch
        results := (result :: !results)
    return (!results |> List.rev |> Seq.collect id |> Array.ofSeq)
}

要使用此功能,我将Async.Parallel替换为batch 20(或其他整数值):

let names =
    asyncWorkflows
    |> batch 20
    |> Async.RunSynchronously

这种方法运行得相当不错,但是我希望有一个系统在一个完成后立即启动每个新的异步,所以在每个上一批N大小完成之后,不是连续批量大小为N,我总是在等待N活跃SqlCommand s(当然,直到我结束)。

问题:

  • 我是否重新发明轮子?换句话说,是否有库函数可以执行此操作? (以某种方式探讨利用ParallelEnumerable.WithDegreeOfParallelism会不会有利可图?)

  • 如果没有,我应该如何实现连续队列而不是一系列不连续的批次?

我主要不是在寻求改进现有代码的建议,但仍会感兴趣和感激地收到这些建议。

1 个答案:

答案 0 :(得分:2)

FSharpx.Control offers an Async.ParallelWithThrottle function。我不确定它是否是it uses SemaphoreSlim的最佳实现。但是易用性非常好,因为我的应用程序不需要顶级性能,所以它对我来说效果很好。虽然它是一个图书馆,如果有人知道如何使它变得更好,那么使图书馆成为最佳表现者总是一件好事,所以我们其他人可以只使用有效的代码并完成我们的工作!