2010-02-08 15 views
7

Ho un processore casella di posta che riceve un numero fisso di messaggi:ordine Garanzia di messaggi inviati ad processore mailbox

let consumeThreeMessages = MailboxProcessor.Start(fun inbox -> 
     async { 
      let! msg1 = inbox.Receive() 
      printfn "msg1: %s" msg1 

      let! msg2 = inbox.Receive() 
      printfn "msg2: %s" msg2 

      let! msg3 = inbox.Receive() 
      printfn "msg3: %s" msg3 
     } 
    ) 

consumeThreeMessages.Post("First message") 
consumeThreeMessages.Post("Second message") 
consumeThreeMessages.Post("Third message") 

Questi messaggi devono essere gestiti esattamente l'ordine inviato. Durante il mio test, esso stampa esattamente quello che dovrebbe:

First message 
Second message 
Third message 

Tuttavia, dal momento che il messaggio distacco è asincrona, suona come inviare 3 messaggi rapidamente potrebbe tradursi in articoli in corso di lavorazione in qualsiasi ordine. Non Per esempio, io non desidera ricevere i messaggi in ordine e ottenere qualcosa di simile:

Second message // <-- oh noes! 
First message 
Third message 

sono i messaggi garantiti da ricevere ed elaborati nell'ordine inviato? O è possibile che i messaggi vengano ricevuti o elaborati fuori servizio?

+0

Questa è davvero una bella domanda. Ho esaminato FSharp.Core.dll ma il codice è difficile da capire come C#. – ChaosPandion

risposta

8

Il codice nella tua funzione consumeThreeMessages verrà sempre eseguito in ordine, a causa del modo in cui i flussi di lavoro asincroni di F # funzionano.

Il seguente codice:

async { 
      let! msg1 = inbox.Receive() 
      printfn "msg1: %s" msg1 

      let! msg2 = inbox.Receive() 
      printfn "msg2: %s" msg2 

     } 

traduce approssimativamente a:

async.Bind(
    inbox.Receive(), 
    (fun msg1 -> 
     printfn "msg1: %s" msg1 
     async.Bind(
      inbox.Receive(), 
      (fun msg2 -> printfn "msg2: %s" msg2) 
     ) 
    ) 
) 

Quando si guarda la forma Dezuccherato, è chiaro che il codice viene eseguito in serie. La parte "asincrona" entra in gioco nell'implementazione di async.Bind, che avvierà il calcolo in modo asincrono e si "sveglierà" al termine dell'esecuzione. In questo modo è possibile sfruttare le operazioni asincrone dell'hardware e non perdere tempo sui thread del sistema operativo in attesa di operazioni IO.

Ciò non significa che non si possano incappare in problemi di concorrenza quando si utilizzano i flussi di lavoro asincroni di F #. Immaginate che avete fatto la seguente:

let total = ref 0 

let doTaskAsync() = 
    async { 
     for i = 0 to 1000 do 
      incr total 
    } |> Async.Start() 

// Start the task twice 
doTaskAsync() 
doTaskAsync() 

Il codice di cui sopra avrà due flussi di lavoro asincroni che modificano lo stesso stato allo stesso tempo.

Quindi, per rispondere alla tua domanda in breve: all'interno del corpo di un singolo blocco asincrono le cose verranno sempre eseguite in ordine. (Ovvero, la riga successiva dopo let let o do! Non viene eseguita fino al completamento dell'operazione asincrona.) Tuttavia, se si condivide lo stato tra due task asincroni, tutte le scommesse sono disattivate. In tal caso, sarà necessario considerare il blocco o l'utilizzo di Concurrent Data Structures fornite con CLR 4.0.

+0

+1, + risposta: ha perfettamente senso :) Sapete, all'inizio non ero sicuro al 100% perché, proprio come un esperimento, volevo implementare i processori di posta in C#, e continuavo a ottenere risultati indesiderati con chiamate sequenziali al mio Implementazione GetMsg (azione >). Le mie chiamate nidificate a 'GetMsg (x => GetMsg (y => GetMsg (z => ...>)))' funzionano bene, ma le chiamate sequenziali 'inbox.GetMsg (x => ...); inbox.GetMsg (x =>); inbox.GetMsg (x => ...) 'viene elaborato senza ordine. Invece di "probabilmente c'è qualcosa di sbagliato nel mio codice", il mio primo pensiero è stato "hey, mi chiedo se F # è rotta?" Per fortuna no;) – Juliet

+0

Sono contento che abbiamo F # guru su SO. Dove altro potremmo girare? – ChaosPandion

+0

Ma il 'Post's può essere riordinato? –

Problemi correlati