2014-09-02 2 views
2

두 개의 비동기 값 (비동기 < _>)에 의해 처음 반환 된 값을 가져 오는 비동기 연산자가 있습니까?첫 번째 비동기 결과 선택

예를 들어, 주어진 두 개의 Async < _> 값 중 하나가 1 초 후에 반환되고 A2가 2 초 후에 반환되는 경우 A1의 결과를 원합니다.

이유는 내가 비동기 시퀀스 인터리브 기능을 구현하려는 것입니다 그래서 (대리석 다이어그램과 같이 시간을 나타내는 공간)과 같이 "정의"이 비동기 순서가있는 경우 : 다음

S1 = -+-----+------------+----+ 
S2 = ---+-------+----------+-----+ 

은 나는이 같은 역할을 새로운 비동기 시퀀스를 생성 할 :

S3 = -+-+---+---+--------+-+--+--+ 

인터리브가 S1 S2는 = S3

그러나이 그렇게를, I 선택 값을 선택하려면 일종의 비동기 선택 연산자가 필요할 것입니다.

나는 두 채널에서 첫 번째 값을 가져올 수있는 Go에서 "선택"과 같을 것이라고 생각합니다.

TPL에는 Task.WhenAny라는 기능이 있습니다. 여기서는 비슷한 기능이 필요할 것입니다.

+1

실제로 비동기식 선택에서 비동기식 인터리브를 구현할 수 있는지 잘 모르겠다. (설명 된대로 선택하면 A1과 A2를 실행하고 A2를 버려야한다.비동기 시퀀스의 유형은 무엇입니까? –

+1

소비자를 [MailboxProcessor] (http://msdn.microsoft.com/en-us/library/ee370357.aspx)로 구현하고 S1과 S2 모두에 메시지를 게시 할 수 있습니까? MailboxProcessor는 S3 순서로 메시지를 가져옵니다. –

+0

유형은 http://tomasp.net/blog/async-sequences.aspx/에서 정의됩니다. 당신은 맞습니다. 나는 다른 계산을 버리고 싶지 않습니다. 단지 완성 된 순서대로 결과를 사용하고 싶습니다. –

답변

3

나는 F # 라이브러리에서 연산자를 사용할 수 없다고 생각합니다. 기존 작업에서이 작업을 결합하려면 Async.StartAsTask을 사용하고 기존 Task.WhenAny 연산자를 사용할 수 있습니다. 그러나 취소와 관련하여 어떻게 작동하는지 정확히 알 수는 없습니다.

Async.Choose 연산자를 F# Snippets web site에 구현하면 다른 옵션을 사용할 수 있습니다. 이것은 특히 우아하지는 않지만 트릭을해야합니다! 답을 독립형으로 만들려면 코드가 아래에 첨부되어 있습니다.

/// Creates an asynchronous workflow that non-deterministically returns the 
/// result of one of the two specified workflows (the one that completes 
/// first). This is similar to Task.WaitAny. 
static member Choose(a, b) : Async<'T> = 
    Async.FromContinuations(fun (cont, econt, ccont) -> 
     // Results from the two 
     let result1 = ref (Choice1Of3()) 
     let result2 = ref (Choice1Of3()) 
     let handled = ref false 
     let lockObj = new obj() 
     let synchronized f = lock lockObj f 

     // Called when one of the workflows completes 
     let complete() = 
     let op = 
      synchronized (fun() -> 
      // If we already handled result (and called continuation) 
      // then ignore. Otherwise, if the computation succeeds, then 
      // run the continuation and mark state as handled. 
      // Only throw if both workflows failed. 
      match !handled, !result1, !result2 with 
      | true, _, _ -> ignore 
      | false, (Choice2Of3 value), _ 
      | false, _, (Choice2Of3 value) -> 
       handled := true 
       (fun() -> cont value) 
      | false, Choice3Of3 e1, Choice3Of3 e2 -> 
       handled := true; 
       (fun() -> 
        econt (new AggregateException 
           ("Both clauses of a choice failed.", [| e1; e2 |]))) 
      | false, Choice1Of3 _, Choice3Of3 _ 
      | false, Choice3Of3 _, Choice1Of3 _ 
      | false, Choice1Of3 _, Choice1Of3 _ -> ignore) 
     op() 

     // Run a workflow and write result (or exception to a ref cell 
     let run resCell workflow = async { 
     try 
      let! res = workflow 
      synchronized (fun() -> resCell := Choice2Of3 res) 
     with e -> 
      synchronized (fun() -> resCell := Choice3Of3 e) 
     complete() } 

     // Start both work items in thread pool 
     Async.Start(run result1 a) 
     Async.Start(run result2 b)) 
3

토마스는 이미 정확한 질문에 대답했습니다. 그러나 F # 용 Hopac 라이브러리가 alternativesConcurrent ML 스타일의 일급, 상위 주문 선택 이벤트를 직접 지원하므로 choose -combinator를 직접 제공하고 Go 's보다 더 풍부한 동시성 추상화 메커니즘을 제공 할 수 있습니다. select 문.

두 개의 비동기 시퀀스를 인터리빙하는 좀 더 구체적인 문제에 관해서는 최근 호파크로 Rx- 스타일 프로그래밍을 수행하는 방법에 대한 아이디어를 실험하기 시작했습니다. 내가 생각해 낸 잠재적 인 접근법 중 하나는 일종의 임시 이벤트 스트림을 정의하는 것입니다. 현재 실험 코드를 찾을 수 있습니다 : 이벤트 스트림 정의 된 작업의 Alts.fs

당신이 볼 수 있듯이 Alts.fsi

    • , 하나 merge입니다. 당신이 찾고있는 것은 의미 상 약간 다를 수 있지만 Hopac 스타일 대안 (또는 동시 ML 스타일 이벤트)을 사용하여 구현하는 것이 쉽습니다.

  • +0

    재미있는 Vesa가 보입니다. 나는 그것으로 실험해야 할 것이다. –

    관련 문제