Compartilhar via


Classe Control.MailboxProcessor<'Msg> (F#)

Um agente de mensagem- processamento que executa uma computação assíncrono.

Namespace/Module Path: Microsoft.FSharp.Control

Assembly: FSharp.Core (em FSharp.Core.dll)

[<Sealed>]
[<AutoSerializable(false)>]
type MailboxProcessor<'Msg> =
 class
  interface IDisposable
  new MailboxProcessor : (MailboxProcessor<'Msg> -> Async<unit>) * ?CancellationToken -> MailboxProcessor<'Msg>
  member this.Post : 'Msg -> unit
  member this.PostAndAsyncReply : (AsyncReplyChannel<'Reply> -> 'Msg) * int option -> Async<'Reply>
  member this.PostAndReply : (AsyncReplyChannel<'Reply> -> 'Msg) * int option -> 'Reply
  member this.PostAndTryAsyncReply : (AsyncReplyChannel<'Reply> -> 'Msg) * ?int -> Async<'Reply option>
  member this.Receive : ?int -> Async<'Msg>
  member this.Scan : ('Msg -> Async<'T> option) * ?int -> Async<'T>
  member this.Start : unit -> unit
  static member Start : (MailboxProcessor<'Msg> -> Async<unit>) * ?CancellationToken -> MailboxProcessor<'Msg>
  member this.TryPostAndReply : (AsyncReplyChannel<'Reply> -> 'Msg) * ?int -> 'Reply option
  member this.TryReceive : ?int -> Async<'Msg option>
  member this.TryScan : ('Msg -> Async<'T> option) * ?int -> Async<'T option>
  member this.add_Error : Handler<Exception> -> unit
  member this.CurrentQueueLength :  int
  member this.DefaultTimeout :  int with get, set
  member this.Error :  IEvent<Exception>
  member this.remove_Error : Handler<Exception> -> unit
 end

Comentários

O agente encapsula uma fila de mensagens que suporta vários gravadores e um único agente do leitor. Criadores envia mensagens ao agente usando o método de postagem e as variações. O agente pode esperar mensagens usando os métodos ou de recebimento de TryReceive ou digitalizar-las entre todas as mensagens disponíveis usando o método ou de verificação de TryScan.

Este tipo é chamado FSharpMailboxProcessor no assembly .NET. Se acessar o tipo de uma linguagem .NET a não ser F#, ou com a reflexão, usa esse nome.

Construtores

Membro

Descrição

novo

Cria um agente. A função de body é usada para gerar a computação assíncrono executada pelo agente. Essa função não é executada Start até que seja chamado.

Métodos como membros exemplo

Membro

Descrição

add_Error

Ocorre quando a execução do levar a uma exceção.

CurrentQueueLength

Retorna o número de mensagens não processadas na fila de mensagens do.

DefaultTimeout

Gera uma exceção de tempo limite se uma mensagem não recebida em essa quantidade de tempo. Nenhum tempo limite é usado por padrão.

Erro

Ocorre quando a execução do levar a uma exceção.

Postagem

Enviar uma mensagem à fila de mensagens de MailboxProcessor, de forma assíncrona.

PostAndAsyncReply

Enviar uma mensagem a um agente e esperam uma resposta no canal, de forma assíncrona.

PostAndReply

Enviar uma mensagem a um agente e esperam uma resposta no canal, de forma síncrona.

PostAndTryAsyncReply

Como AsyncPostAndReply, mas não retornará se nenhuma resposta do tempo limite.

Receber

Para uma mensagem pára. Isso consumirá a primeira mensagem na ordem de chegada.

remove_Error

Ocorre quando a execução do levar a uma exceção.

Verificação

As digitalizações para uma mensagem procurando pelas mensagens na ordem de chegada até scanner retornam um valor. Outras mensagens permanecem na fila.

Iniciar

Inicia o agente.

TryPostAndReply

Como PostAndReply, mas não retornará se nenhuma resposta do tempo limite.

TryReceive

Para uma mensagem pára. Isso consumirá a primeira mensagem na ordem de chegada.

TryScan

As digitalizações para uma mensagem procurando pelas mensagens na ordem de chegada até scanner retornam um valor. Outras mensagens permanecem na fila.

Membros estáticos

Membro

Descrição

Iniciar

Cria e inicia um agente. A função de body é usada para gerar a computação assíncrono executada pelo agente.

Exemplo

O exemplo a seguir mostra o uso mais básico da classe de MailboxProcessor .

open System
open Microsoft.FSharp.Control

type Message(id, contents) =
    static let mutable count = 0
    member this.ID = id
    member this.Contents = contents
    static member CreateMessage(contents) =
        count <- count + 1
        Message(count, contents)

let mailbox = new MailboxProcessor<Message>(fun inbox ->
    let rec loop count =
        async { printfn "Message count = %d. Waiting for next message." count
                let! msg = inbox.Receive()
                printfn "Message received. ID: %d Contents: %s" msg.ID msg.Contents
                return! loop( count + 1) }
    loop 0)

mailbox.Start()

mailbox.Post(Message.CreateMessage("ABC"))
mailbox.Post(Message.CreateMessage("XYZ"))


Console.WriteLine("Press any key...")
Console.ReadLine() |> ignore

A saída de exemplo

  
  
  
  
  
  
  
  
  

O exemplo a seguir mostra como usar MailboxProcessor para criar um agente simples que aceita vários tipos de mensagens e respostas de retornos apropriado. Este agente do servidor representa um fabricante do mercado, que é um agente de compra e de venda em uma bolsa de valores que define a parte preços e oferecem recursos. Os clientes podem ver preços para comprar, ou venda e compartilha.

open System

type AssetCode = string

type Asset(code, bid, ask, initialQuantity) =
    let mutable quantity = initialQuantity
    member this.AssetCode = code
    member this.Bid = bid
    member this.Ask = ask
    member this.Quantity with get() = quantity and set(value) = quantity <- value


type OrderType =
    | Buy of AssetCode * int
    | Sell of AssetCode * int

type Message =
    | Query of AssetCode * AsyncReplyChannel<Reply>
    | Order of OrderType * AsyncReplyChannel<Reply>
and Reply =
    | Failure of string
    | Info of Asset
    | Notify of OrderType

let assets = [| new Asset("AAA", 10.0, 10.05, 1000000);
                new Asset("BBB", 20.0, 20.10, 1000000);
                new Asset("CCC", 30.0, 30.15, 1000000) |]

let codeAssetMap = assets
                   |> Array.map (fun asset -> (asset.AssetCode, asset))
                   |> Map.ofArray

let mutable totalCash = 00.00
let minCash = -1000000000.0
let maxTransaction = 1000000.0

let marketMaker = new MailboxProcessor<Message>(fun inbox ->
    let rec Loop() =
        async {
            let! message = inbox.Receive()
            match message with
            | Query(assetCode, replyChannel) ->
                match (Map.tryFind assetCode codeAssetMap) with
                | Some asset ->
                    printfn "Replying with Info for %s" (asset.AssetCode)
                    replyChannel.Reply(Info(asset))
                | None -> replyChannel.Reply(Failure("Asset code not found."))
            | Order(order, replyChannel) ->
                match order with
                | Buy(assetCode, quantity) ->
                    match (Map.tryFind assetCode codeAssetMap) with
                    | Some asset ->
                        if (quantity < asset.Quantity) then
                            asset.Quantity <- asset.Quantity - quantity
                            totalCash <- totalCash + float quantity * asset.Ask
                            printfn "Replying with Notification:\nBought %d units of %s at price $%f. Total purchase $%f."
                                    quantity asset.AssetCode asset.Ask (asset.Ask * float quantity)
                            printfn "Marketmaker balance: $%10.2f" totalCash
                            replyChannel.Reply(Notify(Buy(asset.AssetCode, quantity)))
                        else
                            printfn "Insufficient shares to fulfill order for %d units of %s."
                                    quantity asset.AssetCode
                            replyChannel.Reply(Failure("Insufficient shares to fulfill order."))
                    | None -> replyChannel.Reply(Failure("Asset code not found."))
                | Sell(assetCode, quantity) ->
                    match (Map.tryFind assetCode codeAssetMap) with
                    | Some asset ->
                        if (float quantity * asset.Bid <= maxTransaction && totalCash - float quantity * asset.Bid > minCash) then
                            asset.Quantity <- asset.Quantity + quantity
                            totalCash <- totalCash - float quantity * asset.Bid
                            printfn "Replying with Notification:\nSold %d units of %s at price $%f. Total sale $%f."
                                    quantity asset.AssetCode asset.Bid (asset.Bid * float quantity)
                            printfn "Marketmaker balance: $%10.2f" totalCash
                            replyChannel.Reply(Notify(Sell(asset.AssetCode, quantity)))
                        else
                            printfn "Insufficient cash to fulfill order for %d units of %s."
                                    quantity asset.AssetCode
                            replyChannel.Reply(Failure("Insufficient cash to cover order."))
                    | None -> replyChannel.Reply(Failure("Asset code not found."))
            do! Loop()
        }
    Loop())

marketMaker.Start()

// Query price. 
let reply1 = marketMaker.PostAndReply(fun replyChannel -> 
    printfn "Posting message for AAA"
    Query("AAA", replyChannel))

// Test Buy Order. 
let reply2 = marketMaker.PostAndReply(fun replyChannel -> 
    printfn "Posting message for BBB"
    Order(Buy("BBB", 100), replyChannel))

// Test Sell Order. 
let reply3 = marketMaker.PostAndReply(fun replyChannel -> 
    printfn "Posting message for CCC"
    Order(Sell("CCC", 100), replyChannel))

// Test incorrect code. 
let reply4 = marketMaker.PostAndReply(fun replyChannel -> 
    printfn "Posting message for WrongCode"
    Order(Buy("WrongCode", 100), replyChannel))

// Test too large a number of shares. 

let reply5 = marketMaker.PostAndReply(fun replyChannel ->
    printfn "Posting message with large number of shares of AAA."
    Order(Buy("AAA", 1000000000), replyChannel))

// Too large an amount of money for one transaction. 

let reply6 = marketMaker.PostAndReply(fun replyChannel ->
    printfn "Posting message with too large of a monetary amount."
    Order(Sell("AAA", 100000000), replyChannel))

let random = new Random()
let nextTransaction() =
    let buyOrSell = random.Next(2)
    let asset = assets.[random.Next(3)]
    let quantity = Array.init 3 (fun _ -> random.Next(1000)) |> Array.sum
    match buyOrSell with
    | n when n % 2 = 0 -> Buy(asset.AssetCode, quantity)
    | _ -> Sell(asset.AssetCode, quantity)

let simulateOne() =
   async {
       let! reply = marketMaker.PostAndAsyncReply(fun replyChannel ->
           let transaction = nextTransaction()
           match transaction with
           | Buy(assetCode, quantity) -> printfn "Posting BUY %s %d." assetCode quantity
           | Sell(assetCode, quantity) -> printfn "Posting SELL %s %d." assetCode quantity
           Order(transaction, replyChannel))
       printfn "%s" (reply.ToString())
    }

let simulate =
    async {
        while (true) do 
            do! simulateOne()
            // Insert a delay so that you can see the results more easily. 
            do! Async.Sleep(1000)
    }

Async.Start(simulate)

Console.WriteLine("Press any key...")
Console.ReadLine() |> ignore

A saída de exemplo

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

Plataformas

O windows 8, Windows 7, Windows Server 2012, Windows Server 2008 R2

Informações de Versão

Versões da biblioteca principal de F#

Suportado em: 2,0, 4,0, portáteis

Consulte também

Referência

Namespace Microsoft.FSharp.Control (F#)