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 言語から、またはリフレクションを使用してこの型にアクセスする場合は、この名前を使用します。
コンストラクター
メンバー |
説明 |
---|---|
エージェントを作成します。関数本体 (body) は、エージェントによって実行される非同期計算の生成に使用されます。この関数は、Start が呼び出されるまで実行されません。 |
インスタンス メンバー
メンバー |
説明 |
---|---|
エージェントを実行した結果、例外が生じたときに発生します。 |
|
エージェントのメッセージ キュー内にある未処理のメッセージ数を返します。 |
|
この時間内にメッセージが受信されない場合に、タイムアウト例外を発生させます。既定では、タイムアウトは使用されません。 |
|
エージェントを実行した結果、例外が生じたときに発生します。 |
|
MailboxProcessor のメッセージ キューにメッセージを非同期的にポストします。 |
|
エージェントにメッセージをポストし、チャネルの応答を非同期的に待機します。 |
|
エージェントにメッセージをポストし、チャネルの応答を同期的に待機します。 |
|
AsyncPostAndReply と似ていますが、タイムアウト期間内に応答がない場合に None を返します。 |
|
メッセージを待機します。最初に到着したメッセージが処理されます。 |
|
エージェントを実行した結果、例外が生じたときに発生します。 |
|
scanner が Some 値を返すまで、到着順にメッセージを検索して、メッセージをスキャンします。他のメッセージはキューに残ります。 |
|
エージェントを開始します。 |
|
PostAndReply と似ていますが、タイムアウト期間内に応答がない場合に None を返します。 |
|
メッセージを待機します。最初に到着したメッセージが処理されます。 |
|
scanner が Some 値を返すまで、到着順にメッセージを検索して、メッセージをスキャンします。他のメッセージはキューに残ります。 |
静的メンバー
メンバー |
説明 |
---|---|
エージェントを作成して開始します。関数本体 (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
出力例
次の例では、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
出力例
プラットフォーム
Windows 8、Windows 7、Windows Server 2012 で Windows Server 2008 R2
バージョン情報
F# コア ライブラリのバージョン
サポート: ポータブル 2.0、4.0