【问题标题】:Is the MailboxProcessor type a replacement for locks?MailboxProcessor 类型是锁的替代品吗?
【发布时间】:2010-02-08 03:18:52
【问题描述】:

我一直在慢慢研究 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()

【问题讨论】:

    标签: concurrency f# message-passing


    【解决方案1】:

    邮箱(和类似结构)用于不使用锁的编程模型,因为它们本质上是围绕异步处理构建的。 (缺少共享可变状态是此模型的另一个要求)。

    Actor 模型可以被认为是一系列单线程迷你应用程序,它们通过相互发送和接收数据进行通信。每个迷你应用程序一次只能由一个线程运行。这与缺少共享状态相结合,使得锁变得不必要。

    过程模型(大多数 OO 代码本质上是过程模型)使用线程级并发和对其他对象的同步调用。 Actor 模型对此进行了翻转——对象之间的调用(消息)是异步的,但每个对象都是完全同步的。

    坦率地说,我没有足够的 F# 来真正分析您的代码。看起来您确实在尝试在邮箱周围贴上一个外观同步的外壳,我想知道这是否真的是最好的事情(与完全采用邮箱模型相比)。在您的实现中,您似乎确实将其用作锁的替代品。

    【讨论】:

    • 你知道你是绝对正确的。当我可以传递一个函数时,我没有理由等待 args 对象。无论如何,需要 args 对象的函数都是异步的。
    【解决方案2】:

    到你问题的第一部分:

    MailboxProcessor 类是在自己的线程上运行的消息队列。你可以发消息 从任何线程同步到 MailboxProcessor。

    这种模型允许线程之间通过消息传递而不是使用锁/互斥体/ipc机制进行通信。

    【讨论】:

    • 典型情况下,在这个模型中,最好将其表述为允许 Actors 之间的通信,而不是线程之间的通信。 Actor 可以由任意线程提供服务,但通常会保证一次仅由单个线程提供服务。演员和线程之间缺乏 1:1 的关系使系统可以扩展到比存在 1:1 映射时更高的“进程”计数。
    猜你喜欢
    • 2011-03-17
    • 1970-01-01
    • 2011-01-11
    • 2010-11-17
    • 2013-09-09
    • 2011-03-29
    • 2019-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多