Asynchronous 使用Async.Parallel合并结果而不重复昂贵的操作?

Asynchronous 使用Async.Parallel合并结果而不重复昂贵的操作?,asynchronous,f#,Asynchronous,F#,假设我有一个同步昂贵的操作: let SomeExpensiveOp():string= System.Console.WriteLine"about to begin expensive OP" System.Threading.Thread.Sleep(TimeSpan.FromSeconds 2.0) System.Console.WriteLine"finished expensive OP" "foo" 将其包装为异步作业: let SomeExpen

假设我有一个同步昂贵的操作:

let SomeExpensiveOp():string=
    System.Console.WriteLine"about to begin expensive OP"
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds 2.0)
    System.Console.WriteLine"finished expensive OP"
    "foo"
将其包装为异步作业:

let SomeExpensiveOpAs():Async<string>=async {
    return SomeExpensiveOp()}

然而,这使得
SomeExpensiveOp
要执行三次。由于上面的
newOp
调用,我希望第二次执行的时间更长,但是我希望
someexpensieopseq
重用对someexpensieop的调用,而不是调用两次。我如何才能实现某个ExpensiveOpSeq,只调用某个ExpensiveOp一次,然后将其重新用于后续结果?

这里的关键观察是
let在每次没有缓存其结果时调用异步表达式。考虑这个例子,在这里我们有了代码> Exop:AsYNC/<代码>,但是我们在一个<代码> AycNC < /C>表达式中等待了三次:

let expOp = SomeExpensiveOpAs()
async {
  let! a = expOp
  let! b = expOp
  let! c = expOp
  return [a;b;c]
} |> Async.RunSynchronously

about to begin expensive OP
finished expensive OP
about to begin expensive OP
finished expensive OP
about to begin expensive OP
finished expensive OP
val it : string list = ["foo"; "foo"; "foo"]
您可以看到每次都对异步操作进行评估。如果您只想执行该昂贵的操作一次,您可以完全评估/等待其结果,并使用该结果,而不是多次等待:

let SomeExpensiveOpSeq():seq<Async<string>>=
    let op = SomeExpensiveOpAs() |> Async.RunSynchronously
    seq {
        for some in [Bar(); Baz()] do
            yield async {
                let! someAfterWaiting = some
                return (String.Concat (op, someAfterWaiting))
            }
    }

这里的关键观察是
let在每次没有缓存其结果时调用异步表达式。考虑这个例子,在这里我们有了代码> Exop:AsYNC/<代码>,但是我们在一个<代码> AycNC < /C>表达式中等待了三次:

let expOp = SomeExpensiveOpAs()
async {
  let! a = expOp
  let! b = expOp
  let! c = expOp
  return [a;b;c]
} |> Async.RunSynchronously

about to begin expensive OP
finished expensive OP
about to begin expensive OP
finished expensive OP
about to begin expensive OP
finished expensive OP
val it : string list = ["foo"; "foo"; "foo"]
您可以看到每次都对异步操作进行评估。如果您只想执行该昂贵的操作一次,您可以完全评估/等待其结果,并使用该结果,而不是多次等待:

let SomeExpensiveOpSeq():seq<Async<string>>=
    let op = SomeExpensiveOpAs() |> Async.RunSynchronously
    seq {
        for some in [Bar(); Baz()] do
            yield async {
                let! someAfterWaiting = some
                return (String.Concat (op, someAfterWaiting))
            }
    }

为什么不先对它求值一次,然后在必要时使用它的值呢
因为有些ExpensiveOP实际上接收参数,但也许我在问题
中对我的示例简化得太多了为什么不先对它求值一次,然后在必要时使用它的值呢
因为有些ExpensiveOP实际上接收参数,但也许我在问题中过于简化了我的例子
let SomeExpensiveOpSeq op : seq<Async<string>>=
    seq {
        for some in [Bar(); Baz()] do
            yield async {
                let! someAfterWaiting = some
                return (String.Concat (op, someAfterWaiting))
            }
    }

let DoSomething() =
    let newOp = SomeExpensiveOpAs() |> Async.RunSynchronously
    let someAsyncOps = SomeExpensiveOpSeq newOp |> Async.Parallel |> Async.RunSynchronously
    let allStrings = newOp::(List.ofArray someAsyncOps)
    for str in allStrings do
        Console.WriteLine str
    Console.WriteLine()

> DoSomething();;
about to begin expensive OP
finished expensive OP
foo
foobar
foobaz