2012-06-04 14 views
5

Görev <T> kullanırken, görevin yerine getirilmesi sırasında bir istisna Task.Wait(); F # MailBoxProcessor kullanırken, istisna yutulur ve this question uyarınca açık bir şekilde ele alınması gerekir.Birleştirme Görev <T> ve F # MailboxProcessor özel durum işleme

Bu fark, bir Görev aracılığıyla F # aracısını C# koduna maruz bırakmayı zorlaştırır. Örneğin, bu madde:

type internal IncrementMessage = 
    Increment of int * AsyncReplyChannel<int> 

type IncrementAgent() = 
    let counter = Agent.Start(fun agent -> 
     let rec loop() = async { let! Increment(msg, replyChannel) = agent.Receive() 
           match msg with 
           | int.MaxValue -> return! failwith "Boom!" 
           | _ as i -> replyChannel.Reply (i + 1) 
              return! loop() } 

     loop()) 

    member x.PostAndAsyncReply i = 
     Async.StartAsTask (counter.PostAndAsyncReply (fun channel -> Increment(i, channel))) 
C# çağrılabilir, ancak istisna C# iade edilmez: Bunun yerine özel durum alma

[Test] 
public void ExceptionHandling() 
{ 
    // 
    // TPL exception behaviour 
    // 
    var task = Task.Factory.StartNew<int>(() => { throw new Exception("Boom!"); }); 

    try 
    { 
     task.Wait(); 
    } 
    catch(AggregateException e) 
    { 
     // Exception available here 
     Console.WriteLine("Task failed with {0}", e.InnerException.Message); 
    } 

    // 
    // F# MailboxProcessor exception behaviour 
    // 
    var incAgent = new IncrementAgent(); 
    task = incAgent.PostAndAsyncReply(int.MaxValue); 

    try 
    { 
     task.Wait(); // deadlock here 
    } 
    catch (AggregateException e) 
    { 
     Console.WriteLine("Agent failed with {0}", e.InnerException.Message); 
    } 
} 

, C# kod sadece task.Wait askıda kalıyor(). F # ajanının bir görev gibi davranmasını sağlamanın bir yolu var mı? Değilse, F # ajanlarının diğer .NET kodlarına maruz kalmasında sınırlı bir kullanım olduğu görülüyor.

cevap

3

İşlenmenin bir yolu, aracın bir hata durumuyla birlikte bir DU döndürmesidir. Sonra istisnayı aracının dışından yükseltebilirsiniz.

type internal IncrementResponse = 
    | Response of int 
    | Error of exn 

type internal IncrementMessage = 
    | Increment of int * AsyncReplyChannel<IncrementResponse> 

type IncrementAgent() = 
    let counter = Agent.Start(fun agent -> 
     let rec loop() = 
      async { 
      let! Increment(msg, replyChannel) = agent.Receive() 
      match msg with 
      | int.MaxValue -> replyChannel.Reply (Error (Failure "Boom!")) 
      | _ as i -> replyChannel.Reply (Response(i + 1)) 
      return! loop() 
      } 
     loop()) 

    member x.PostAndAsyncReply i = 
     Async.StartAsTask (
      async { 
      let! res = counter.PostAndAsyncReply (fun channel -> Increment(i, channel)) 
      match res with 
      | Response i -> return i 
      | Error e -> return (raise e) 
      } 
     ) 
+0

Teşekkürler, tam olarak aradığım şey! Kodumun benzer bir dönüşü kullanmasına rağmen, başımın etrafında dönme (e) 'yi yasal bir yapı olarak görmüyorum. "Boom!" ile başarısız. – Akash

+0

'raise'' 'T' döndürme olarak tanımlanır, ancak elbette hiçbir zaman gerçekten geri dönmez. Bu, ifade ifadesinin türüne bakılmaksızın herhangi bir yerde kullanılmasına izin verir. – Daniel

+0

@Akash: Ayrıca şunları yazabilirsiniz: 'Response match res ile Response i -> i | Hata e -> e'yi yükselt. – Daniel