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 Windows 서버 2012, Windows Server 2008 R2, Windows 7, 8
버전 정보
F# 코어 라이브러리 버전
지원: 2.0, 4.0, 노트북