2010-02-08 4 views
3

Я медленно изучал все функции, которые F # приносит в таблицу. Меня особенно заинтересовал MailboxProcessor.Является ли 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() 

ответ

5

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

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

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

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

+0

Вы знаете, что абсолютно правы. У меня нет причин ждать объекта args, когда я могу передать функцию. Функции, которые нуждаются в объекте args, в любом случае являются асинхронными. – ChaosPandion

0

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

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

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

+2

Typicall, в этой модели, лучше сказать, что позволяет общаться между Актерами, а не потоками. Актеры могут обслуживаться произвольными потоками, но обычно гарантируют, что они будут обслуживаться только одним потоком за раз. Отсутствие соотношения между субъектами и потоками 1: 1 позволяет системе масштабироваться до гораздо более высокого уровня «процесса», чем если бы существовало отображение 1: 1. – kyoryu

Смежные вопросы