本頁是否能提供幫助?
您對此內容的意見反應十分重要。 請告訴我們您的想法。
其他意見反應?
剩餘 1500 個字元
匯出 (0) 列印
全部展開
本文章是由人工翻譯。 將指標移到文章內的文字上方即可查看原文。 其他資訊。
譯文
原文

Control.MailboxProcessor<'Msg> 類別 (F#)

可執行非同步計算的訊息處理代理程式。

命名空間/模組路徑:Microsoft.FSharp.Control

組件:FSharp.Core (在 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

此代理程式會封裝可支援多重寫入器和單一讀取器代理程式的訊息佇列。 寫入器會使用 Post 方法與其變數,傳送訊息給代理程式。 此代理程式可以使用 Receive 或 TryReceive 方法等待訊息,或是使用 Scan 或 TryScan 方法掃描所有的可用訊息。

這個型別在 .NET 組件中名為 FSharpMailboxProcessor 如果是透過 F# 以外的 .NET 語言存取型別,或透過反映存取型別,請使用這個名稱。

成員

描述

new

建立代理程式。 body 函式會用來產生由代理程式執行的非同步計算。 在呼叫 Start 之前不會執行這個函式。

成員

描述

add_Error

執行代理程式而造成例外狀況時發生。

CurrentQueueLength

傳回代理程式之訊息佇列中的未處理訊息數目。

DefaultTimeout

如果未在此時間內收到訊息,則會引發逾時例外狀況。 預設不會使用逾時。

錯誤

執行代理程式而造成例外狀況時發生。

Post

非同步將訊息發佈至 MailboxProcessor 的訊息佇列。

PostAndAsyncReply

非同步將訊息發佈至代理程式,並在通道上等待回覆。

PostAndReply

同步將訊息發佈至代理程式,並在通道上等待回覆。

PostAndTryAsyncReply

與 AsyncPostAndReply 類似,但會在未於逾時期間內回覆時傳回 None。

Receive

等待訊息。 這將會使用第一個收到的訊息。

remove_Error

執行代理程式而造成例外狀況時發生。

Scan

在多個訊息內依照收到的順序掃描訊息,直到 scanner 傳回 Some 這個值為止。 其他訊息仍在佇列中。

Start

啟動代理程式。

TryPostAndReply

與 PostAndReply 類似,但會在未於逾時期間內回覆時傳回 None。

TryReceive

等待訊息。 這將會使用第一個收到的訊息。

TryScan

在多個訊息內依照收到的順序掃描訊息,直到 scanner 傳回 Some 這個值為止。 其他訊息仍在佇列中。

成員

描述

Start

建立並啟動代理程式。 body 函式會用來產生由代理程式執行的非同步計算。

下列範例示範 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


範例輸出

            
              按任意鍵...
            
            
              訊息計數 = 0。
            
            
              等待下一則訊息。
            
            
              接收到的訊息。
            
            
              ID:1 內容:ABC 訊息計數= 1。
            
            
              等待下一則訊息。
            
            
              接收到的訊息。
            
            
              ID:2 內容:XYZ 訊息計數= 2。
            
            
              等待下一則訊息。
            
          

下列範例示範如何使用 MailboxProcessor,建立簡單的代理程式以接受各種類型的郵件並傳回適當的回覆。 這個伺服器代理程式代表證券交易商,是為資產設定買入價和賣出價的證券交易所買賣經紀人。 用戶端可以查詢價格,或購買和賣出股票。


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


範例輸出

            
              以及 AAA 等級的電子郵件訊息回覆與 AAA 電子郵件訊息的詳細資訊 BBB 的回覆連同告知:BBB 佔 100 個單位以價格$20.100000。
            
            
              總購買金額 $2010.000000。
            
            
              Broker 人員餘額:$ 2010.00 CCC 電子郵件訊息的回覆連同告知:外部 CCC 100 個單位以價格$30.000000。
            
            
              總銷售金額 $3000.000000。
            
            
              Broker 人員餘額:$ -990.00 WrongCode 電子郵件訊息的電子郵件訊息與 AAA 共用的最大數字。
            
            
              股票不足,無法履行 1000000000 個單位的 AAA 的訂單。
            
            
              發佈貨幣金額太大的訊息。
            
            
              現金不足,無法履行 100000000 個單位的 AAA 的訂單。
            
            
              按任意鍵...
            
            
              Posting BUY BBB 1338。
            
            
              回覆連同告知:CCC 佔 1338 個單位以價格$30.150000。
            
            
              總購買金額 $40340.700000。
            
            
              Broker 人員餘額:$ 39350.70 Program+Snippet3+Reply+Notify 張貼購買 BBB 1961 年。
            
            
              回覆連同告知:BBB 佔 1961 個單位以價格$20.100000。
            
            
              總購買金額 $39416.100000。
            
            
              Broker 人員餘額:$ 78766.80
            
          

Windows 8 中, Windows 7, Windows Server 2012 上, Windows Server 2008 R2

F# 核心程式庫版本

支援版本:2.0, 4.0,可攜式執行檔 (PE)。

社群新增項目

顯示:
© 2015 Microsoft