2
私は、非同期関数がサーバー(つまりマルチスレッド環境)上で一度しか実行されないようにする関数(OnceAsync f)を記述しようとしています。 (ロック、ビジー待機!!)私はそれが簡単だろうと思ったが、それはすぐに複雑になったOnceAsync:正確に一度f#async関数を実行する
これが私の解決策ですが、私はそれが設計上-だと思います。より良い方法が必要です。これは、FSIで動作するはずです:OnceAsyncが正しいかどう
let locked_counter init =
let c = ref init
fun x -> lock c <| fun() ->
c := !c + x
!c
let wait_until finished = async {
while not(finished()) do
do! Async.Sleep(1000)
}
let OnceAsync f =
// - ensure that the async function, f, is only called once
// - this function always returns the value, f()
let mutable res = None
let lock_inc = locked_counter 0
async {
let count = lock_inc 1
match res, count with
| None, 1 -> // 1st run
let! r = f
res <- Some r
| None, _ -> // nth run, wait for 1st run to finish
do! wait_until (fun() -> res.IsSome)
| _ ->() // 1st run done, return result
return res.Value
}
あなたがテストするには、このコードを使用することができます:それは収まる場合
let test() =
let mutable count = 0
let initUser id = async {
do! Async.Sleep 1000 // simulate work
count <- count + 1
return count
}
//let fmem1 = (initUser "1234")
let fmem1 = OnceAsync (initUser "1234")
async {
let ps = Seq.init 20 (fun i -> fmem1)
let! rs = ps |> Async.Parallel
printfn "rs = %A" rs // outputs: [|1; 1; 1; 1; 1; ....; 1|]
}
test() |> Async.Start
ナイス!私はロックや待っているから離れようとしていたが、これは私が持っているものよりはるかに優れている。 – Ray
@Ray私は 'MailboxProcessor'のサンプルを追加しましたが、IIRC、それはボンネットの下にロックを使用しないので、あなたは完全に彼らから得ることができるかどうかはわかりません。 –
私はロックから離れることができないように見えます。 TaskCompletionSourceソリューションが機能します。私はInterlocked.Incrementを使うことはできません。私は自分のバージョンを作るだけです。 – Ray