Pregunta

Quiero implementar el concepto de un puerto del marco CCR en F# (ya que CCR no es compatible oficialmente para .NET 4.0). Sé que uno puede usar el Procesador de buzones Clase en f# para hacer esto. Esto funciona perfectamente para simple Recibir Árbitros pero necesito el concepto de Entrelazar Árbitro, es decir, quiero controlar qué mensajes se procesan exclusivamente y cuáles se procesan simultáneamente. Hasta ahora no tengo idea de implementar esto en F# y estaría agradecido por su ayuda.

¿Fue útil?

Solución

No estoy muy familiarizado con CCR, pero intentaré responder; entendiendo que es que entrelazarse se comporta un poco como ReaderWriterLock. Es decir, puede especificar algunas operaciones que pueden ejecutarse en paralelo (lecturas) y algunas operaciones que son exclusivas (escrituras).

El siguiente agente es una forma de implementarlo (no probado, pero escriba verificaciones :-)). El agente expone dos operaciones destinadas al uso público. El último es interno:

type Message<'T> =
  | PerformReadOperation of ('T -> Async<unit>)
  | PerformWriteOperation of ('T -> Async<'T>)
  | ReadOperationCompleted
  • Enviando al agente PerformReadOperation, le está dando una operación que debe ejecutarse (una vez) utilizando el estado y posiblemente en paralelo con otras operaciones de lectura.

  • Enviando al agente PerformWriteOperation, le está dando una operación que calcula un nuevo estado y debe ejecutarse después de todas las operaciones de lectura completas. (Si estuviera trabajando con un estado inmutable, eso simplificaría las cosas, ¡no tendría que esperar hasta que los lectores completen! Pero la implementación a continuación implementa la espera).

El agente comienza con algún estado inicial:

let initial = // initial state

Y el resto del agente se implementa utilizando dos bucles:

let interleaver = MailboxProcessor.Start(fun mbox ->

  // Asynchronously wait until all read operations complete
  let rec waitUntilReadsComplete reads = 
    if reads = 0 then async { return () }
    else mbox.Scan(fun msg ->
      match msg with
      | ReadOperationCompleted -> Some(waitUntilReadsComplete (reads - 1))
      | _ -> None)

  let rec readingLoop state reads = async {
    let! msg = mbox.Receive()
    match msg with
    | ReadOperationCompleted ->
        // Some read operation completed - decrement counter
        return! readingLoop state (reads - 1) 
    | PerformWriteOperation(op) ->
        do! waitUntilReadsComplete reads
        let! newState = op state
        return! readingLoop newState 0
    | PerformReadOperation(op) ->
        // Start the operation in background & increment counter
        async { do! op state
                mbox.Post(ReadOperationCompleted) }
        |> Async.Start
        return! readingLoop state (reads + 1) }
  readingLoop initial 0)

Otros consejos

Solo para agregar a la solución sugerida por Tomas, en caso de que no desee exponer el mensaje "Readoperation se compleja" al consumidor del cuadro de correo (ya que este mensaje es interno y en la implementación actual puede ser enviado por cualquier consumidor del cuadro de correo) Se puede crear un cuadro de correo separado dentro de la función del procesador de la casilla de correo principal que aceptará dos mensajes: ReadoperationCompleted y WaitForReadCompleted (este se usará con PostandasynCreply por el cuadro de correo principal), ya que la respuesta a este mensaje solo vendrá cuando toda la lectura Las operaciones se completan. Además, el recuento de "lectura" representado por "lecturas" se trasladará a este nuevo buzón interno, ya que ese estado se encapsula con este buzón interno.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top