Concurrency MailboxProcessor类型是锁的替代品吗?
我一直在慢慢地研究F#带来的所有特性。特别引起我兴趣的是Concurrency MailboxProcessor类型是锁的替代品吗?,concurrency,f#,message-passing,Concurrency,F#,Message Passing,我一直在慢慢地研究F#带来的所有特性。特别引起我兴趣的是MailboxProcessor 在C#中,与此类似的代码最有可能使用锁。我们可以考虑邮件处理程序< /代码>作为锁的替换吗? 在下面的例子中,我在做什么 任何特别幼稚的事情都可以 你看到什么了吗 改善了吗 module Tcp = open System open System.Collections.Generic open System.Net open System.Net.Sockets
MailboxProcessor
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()
模块Tcp=
开放系统
open System.Collections.Generic
开放系统.Net
开放式System.Net.Sockets
开放系统。线程
输入SocketAsyncMessage=
|AsyncReplyChannel的获取
|SocketAsyncEventArgs的放置
|处置AsyncReplyChannel
类型SocketAsyncEventArgsPool(大小:int)=
让代理=
惰性(MailboxProcessor.Start(
(趣味收件箱->
let references=lazy(新列表(大小))
让idleReferences=lazy(新队列(大小))
让rec循环()=
异步的{
let!message=inbox.Receive()
将消息与匹配
|获取频道->
如果idleReferences.Value.Count>0,则
channel.Reply(idleReferences.Value.Dequeue())
其他的
设args=new SocketAsyncEventArgs()
references.Value.Add参数
频道.回复参数
return!loop()
|放置参数->
如果args=null,则
空参数“args”
elif references.Value.Count<大小
idleReferences.Value.Enqueue参数
其他的
如果不是(references.Value.Remove args),则
未找到invalidOp“引用”
args.Dispose()
return!loop()
|处置频道->
如果创建了references.isvalues,则
参考文献。价值
|>Seq.iter(fun args->args.Dispose())
频道。回复收件箱
}
循环()))
///从池中返回SocketAsyncEventArgs实例。
这个成员。获取()=
agent.Value.PostAndReply(趣味频道->获取频道)
///将SocketAsyncEventArgs实例返回到池中。
成员,请输入args=
agent.Value.Post(放置参数)
///释放SocketAsyncEventArgsPool使用的所有资源。
成员。处置()=
(此:>IDisposable.Dispose())
接口IDisposable与
成员this.Dispose()=
如果已创建agent.isvalues,则
(agent.Value.PostAndReply(趣味频道->Dispose频道):>IDisposable.Dispose()
邮箱(以及类似的结构)用于不使用锁的编程模型中,因为它们本质上是围绕异步处理构建的。(缺少共享的可变状态是该模型的另一个要求)
Actor模型可以看作是一系列单线程微型应用程序,它们通过相互发送和接收数据进行通信。每个迷你应用程序一次只能由一个线程运行。这再加上缺少共享状态,使得锁变得不必要
过程模型(大多数OO代码的核心是过程性的)使用线程级并发和对其他对象的同步调用。参与者模型改变了这一点——对象之间的调用(消息)是异步的,但每个对象都是完全同步的
坦白地说,我不知道足够的F#来真正分析你的代码。看起来您确实在尝试在邮箱周围粘贴一个同步外壳,我想知道这是否是最好的做法(与完全采用邮箱模型相比)。在您的实现中,似乎您正在使用它来替换锁。对于问题的第一部分: MailboxProcessor类是一个在自己的线程上运行的消息队列。你可以发个短信 从任何线程异步或同步发送到MailboxProcessor
这种模型允许线程之间通过消息传递进行通信,而不是使用锁/互斥锁/ipc机制。你知道你是绝对正确的。当我可以传递函数时,我没有理由等待args对象。无论如何,需要args对象的函数都是异步的。在这个模型中,最好说它允许参与者之间的通信,而不是线程之间的通信。参与者可以由任意线程提供服务,但通常保证一次只能由单个线程提供服务。参与者和线程之间缺乏1:1的关系,这使得系统可以扩展到比存在1:1映射时高得多的“进程”计数。