내보내기(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입니다.
              
              
                Marketmaker 잔액: $ 2010.00 게시 메시지 CCC 회신 알림 사용에 대 한: 가격 $30.000000 CCC의 판매 100 단위.
              
              
                매출 총액은 $3000.000000입니다.
              
              
                Marketmaker 잔액: $-990.00 WrongCode 게시 메시지 많은 수의 공유에 대 한 메시지를 게시 합니다.
              
              
                AAA 100000000개 주문을 처리하기 위한 주식이 부족합니다.
              
              
                통화량이 너무 큰 메시지를 게시합니다.
              
              
                AAA 100000000개 주문을 처리하기 위한 현금이 부족합니다.
              
              
                아무 키를 누르십시오...
              
              
                BUY CCC 1338을 게시합니다.
              
              
                알림 메시지와 회신: 1338 CCC 단위 가격 $30.150000를 구입 합니다.
              
              
                구매 총액은 $40340.700000입니다.
              
              
                Marketmaker 잔액: $ 39350.70 프로그램 + Snippet3 + 회신 + 구매 BBB 1961 게시 알림.
              
              
                알림 메시지와 회신: BBB 1961 단위 가격 $20.100000를 구입 합니다.
              
              
                구매 총액은 $39416.100000입니다.
              
              
                Marketmaker 잔액: $ 78766.80
              
            

Windows Windows 서버 2012, Windows Server 2008 R2, Windows 7, 8

F# 코어 라이브러리 버전

지원: 2.0, 4.0, 노트북

커뮤니티 추가 항목

Microsoft는 MSDN 웹 사이트에 대한 귀하의 의견을 이해하기 위해 온라인 설문 조사를 진행하고 있습니다. 참여하도록 선택하시면 MSDN 웹 사이트에서 나가실 때 온라인 설문 조사가 표시됩니다.

참여하시겠습니까?
표시:
© 2015 Microsoft