2010-11-02 13 views
7

Questa domanda unisce due temi che non capisco pienamenteAgente/MailboxProcessor in C# utilizzando nuova async/attendono

lettura attraverso un paper su async in F #, mi sono imbattuto in tema di Agenti/MailboxProcessors, che può essere utilizzato per implementare macchine a stato reattivo. La nuova funzionalità asincrona/attesa in C# 5 potrebbe essere utilizzata per implementare qualcosa di simile in C#, oppure esiste già qualcosa di analogo che sarebbe più adatto?

+0

Potrebbe risolvere il collegamento interrotto? – czifro

+0

@czifro, ok fatto. – Benjol

risposta

3

In linea di principio, mi aspetto che sia semplice tradurre queste API F # in C# -plus-async-await.

In pratica, non sono chiaro se risultasse bello, o brutto e pieno di annotazioni di tipo extra, o semplicemente non-idiomatico e che necessitasse di un massaggio API per farlo sentire più a suo agio in C#. Penso che la giuria sia fuori fino a quando qualcuno fa il lavoro e ci prova. (Presumo che non ci sia un campione del genere nell'attesa CTP.)

+2

Scaricato il CTP. Ora procederò ad espandere la mia ignoranza provando :) – Benjol

+0

Vedo il tuo Super Breakaway fatto nei campioni, congratulazioni :) – Benjol

+1

Ouch! Troppo difficile per me questa roba :( – Benjol

11

Con un po 'di hacking piuttosto orribile, è possibile utilizzare il tipo MailboxProcessor da C# utilizzando async. Alcune difficoltà sono che il tipo utilizza alcune caratteristiche specifiche di F # (gli argomenti facoltativi sono opzioni, le funzioni sono FSharpFunc, ecc.)

Tecnicamente, la più grande differenza è che F # async viene disattivato mentre C# async crea un'attività già in esecuzione . Ciò significa che per costruire F # async da C#, è necessario scrivere un metodo che richiede unt -> Task<T> e crea Async<T>. Ho scritto un blog post that discusses the difference.

Anwyay, se si vuole sperimentare, qui è un codice è possibile utilizzare:

static FSharpAsync<T> CreateAsync<T>(Func<Task<T>> f) 
{ 
    return FSharpAsync.FromContinuations<T>(
    FuncConvert.ToFSharpFunc< 
     Tuple< FSharpFunc<T, Unit>, 
      FSharpFunc<Exception, Unit>, 
      FSharpFunc<OperationCanceledException, Unit> >>(conts => { 
    f().ContinueWith(task => { 
     try { conts.Item1.Invoke(task.Result); } 
     catch (Exception e) { conts.Item2.Invoke(e); } 
    }); 
    })); 
} 

static void MailboxProcessor() { 
    var body = FuncConvert.ToFSharpFunc< 
       FSharpMailboxProcessor<int>, 
       FSharpAsync<Unit>>(mbox => 
    CreateAsync<Unit>(async() => { 
     while (true) { 
     var msg = await FSharpAsync.StartAsTask 
      (mbox.Receive(FSharpOption<int>.None), 
      FSharpOption<TaskCreationOptions>.None, 
      FSharpOption<CancellationToken>.None); 
     Console.WriteLine(msg); 
     } 
     return null; 
    })); 
    var agent = FSharpMailboxProcessor<int>.Start(body, 
       FSharpOption<CancellationToken>.None); 
    agent.Post(1); 
    agent.Post(2); 
    agent.Post(3); 
    Console.ReadLine(); 
} 

Come si può vedere, questo sembra davvero orribile :-).

  • In linea di principio, potrebbe essere possibile scrivere un C# involucro accogliente per il tipo di MailboxProcessor (basta estrarre i bit brutte di questo codice), ma ci sono alcuni problemi.

  • In F # si utilizzano spesso async ricorsive per implementare la macchina a stati nel processore di cassette postali. Se scrivi la stessa cosa in C#, alla fine otterrai StackOverflow, quindi dovresti scrivere loop con stato mutabile.

  • È perfettamente possibile scrivere l'agente in F # e chiamarlo da C#. Questo è solo questione di esporre l'interfaccia C# -friendly da F # (usando il metodo Async.StartAsTask).

+0

Ahi, i miei occhi :) Il 'Task -> Async ' è stato proprio il pezzo su cui sono rimasto bloccato, quindi vedrò quello.Che ne dici di riscrivere un MailboxProcessor in C#, o semplicemente non ne vale la pena a causa del tuo secondo punto in merito alla ricorsione? – Benjol

+0

@Benjol - Ho scritto un tipo simile in C#. Erano circa 300 righe di codice. La differenza è un'API semplificata. – ChaosPandion

+0

@Benjol: Penso che anche la riscrittura di MailboxProcessor in C# sia un'opzione. Non è facile ottenere il blocco e la sincronizzazione corretti (specialmente per il metodo 'TryScan'), ma penso che dovrebbe funzionare (puoi sempre evitare la ricorsione, ma nel caso di _state machines_ il codice sembra piuttosto scadente, quindi è spiacevole se questo è ciò che gli utenti devono scrivere) –

0

Si potrebbe dare un'occhiata a Stact. Non è stato aggiornato tra un po ', ma se volessi creare qualcosa con un supporto C# leggermente migliore, potresti trovarlo come un buon punto di partenza. Non penso che sia aggiornato con async/await, però.

Problemi correlati