Является ли тип MailboxProcessor заменой замков? - PullRequest
3 голосов
/ 08 февраля 2010

Я медленно изучаю все функции, которые F # вносит в таблицу. Тот, который особенно пробудил мой интерес, это MailboxProcessor.

  1. Эквивалент этого в C #, скорее всего, будет использовать блокировки. Можем ли мы считать MailboxProcessor заменой замков?
  2. В следующем примере я делаю что-нибудь особенно наивное или может Вы видите все, что может быть улучшилось?


module Tcp =
    open System
    open System.Collections.Generic
    open System.Net
    open System.Net.Sockets
    open System.Threading    

    type SocketAsyncMessage =
        | Get of AsyncReplyChannel<SocketAsyncEventArgs>
        | Put of SocketAsyncEventArgs
        | Dispose of AsyncReplyChannel<MailboxProcessor<SocketAsyncMessage>>

    type SocketAsyncEventArgsPool(size:int) =             
        let agent = 
            lazy(MailboxProcessor.Start(
                    (fun inbox ->
                        let references = lazy(new List<SocketAsyncEventArgs>(size))       
                        let idleReferences = lazy(new Queue<SocketAsyncEventArgs>(size))                    
                        let rec loop () = 
                            async {
                                let! message = inbox.Receive()
                                match message with
                                | Get channel -> 
                                    if idleReferences.Value.Count > 0 then
                                        channel.Reply(idleReferences.Value.Dequeue())
                                    else    
                                        let args = new SocketAsyncEventArgs()
                                        references.Value.Add args
                                        channel.Reply args  
                                    return! loop()
                                | Put args ->
                                    if args = null then
                                        nullArg "args" 
                                    elif references.Value.Count < size then
                                        idleReferences.Value.Enqueue args
                                    else                                       
                                        if not(references.Value.Remove args) then
                                            invalidOp "Reference not found."                                        
                                        args.Dispose() 
                                    return! loop()
                                | Dispose channel ->
                                    if references.IsValueCreated then
                                        references.Value 
                                        |> Seq.iter(fun args -> args.Dispose())
                                    channel.Reply inbox 
                            }
                        loop())))

        /// Returns a SocketAsyncEventArgs instance from the pool.         
        member this.Get () =
            agent.Value.PostAndReply(fun channel -> Get channel)            
        /// Returns the SocketAsyncEventArgs instance to the pool. 
        member this.Put args =
            agent.Value.Post(Put args)
        /// Releases all resources used by the SocketAsyncEventArgsPool.
        member this.Dispose () =
            (this:>IDisposable).Dispose()  

        interface IDisposable with
             member this.Dispose() =
                if agent.IsValueCreated then
                    (agent.Value.PostAndReply(fun channel -> Dispose channel):>IDisposable).Dispose()

Ответы [ 2 ]

5 голосов
/ 08 февраля 2010

Почтовые ящики (и подобные конструкции) используются в моделях программирования, в которых не используются блокировки, поскольку они изначально построены на асинхронной обработке.(Отсутствие общего изменяемого состояния является еще одним требованием этой модели.)

Модель Actor можно рассматривать как серию однопоточных мини-приложений, которые обмениваются данными, отправляя и получая данные друг от друга.Каждое мини-приложение будет одновременно запускаться только одним потоком.Это, в сочетании с отсутствием общего состояния, делает ненужными блокировки.

Процедурные модели (и большая часть кода ОО по своей сути процедурные) используют параллелизм на уровне потоков и синхронные вызовы других объектов.Модель Actor полностью изменяет ситуацию - вызовы (сообщения) между объектами асинхронны, но каждый объект полностью синхронен.

Я не знаю достаточно F #, чтобы откровенно проанализировать ваш код.Похоже, вы пытаетесь прикрепить оболочку синхронного вида к вашему почтовому ящику, и мне интересно, действительно ли это лучше всего делать (вместо полного охвата модели почтового ящика).В вашей реализации кажется, что вы используете его в качестве замены для блокировки.

0 голосов
/ 08 февраля 2010

К первой части вашего вопроса:

Класс MailboxProcessor - это очередь сообщений, работающая в своем собственном потоке. Вы можете отправить сообщение к MailboxProcessor из любого потока как асинхронно, так и синхронно.

Такая модель позволяет связываться между потоками посредством передачи сообщений вместо использования механики блокировок / мьютексов / ipc.

...