Najprostsza możliwa realizacja mogłem pomyśleć wyglądać tak:
open FSharp.Control
let getOneOrOther() =
let queue = BlockingQueueAgent(1)
let async1 = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
do! queue.AsyncAdd(1) } |> Async.Start
let async2 = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
do! queue.AsyncAdd(2) } |> Async.Start
queue.Get()
for i in 1..10 do
printfn "%d" <| getOneOrOther()
Console.ReadLine() |> ignore
Opiera się on realizacji kolejki blokowanie z projektu FSharpx, które prawdopodobnie zechcesz z innych powodów. Ale jeśli nie chcesz żadnych zależności, zawiera także kolejkę blokującą, z nieco mniej ładnym interfejsem.
Aby uzyskać bardziej ogólną wersję z wbudowanym anulowaniem, poniższa wersja pobiera Seq<unit -> Async<'T>>
i zwraca pierwszy wynik, przez który przechodzi, anulując wszystkie pozostałe.
open FSharp.Control
open System.Threading
let async1() = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
return 1 }
let async2() = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
return 2 }
let getFirst asyncs =
let queue = BlockingQueueAgent(1)
let doWork operation = async {
let! result = operation()
do! queue.AsyncAdd(result) }
let start work =
let cts = new CancellationTokenSource()
Async.Start(work, cts.Token)
cts
let cancellationTokens =
asyncs
|> Seq.map doWork
|> Seq.map start
let result = queue.Get()
cancellationTokens
|> Seq.iter (fun cts -> cts.Cancel(); cts.Dispose())
result
for i in 1..10 do
printfn "%A" <| getFirst [async1;async2]
Console.ReadLine() |> ignore
Czy spodziewasz się, że inne zadania zostaną przerwane, lub po prostu pozwolisz im pozostać w tle? – mavnn
Nie chcę ich przerwać, ale być może w przyszłości chcę rozszerzyć tę metodę na coś takiego jak WaitForAnySuccessfullAndFaiIfAllFail. Jestem nowy w f #. W scala świecie było to dość proste, aby wdrożyć to z przyszłością i obietnicami. –