Concurrency 为协调写入/读取选择正确的Clojure引用类型
我正在为Campfire开发一个聊天机器人,它在atom中保存当前用户列表,Concurrency 为协调写入/读取选择正确的Clojure引用类型,concurrency,clojure,stm,Concurrency,Clojure,Stm,我正在为Campfire开发一个聊天机器人,它在atom中保存当前用户列表,(defonce users(atom{})) 我最初选择这个引用类型是因为它的简单性,到目前为止它运行良好,但这可能需要改变 Campfire向流式api发送EnterMessage和LeaveMessage事件。我的机器人通过从CampfireAPI获取当前用户列表,然后调用reset 这些相同的进入/离开事件触发随机交互,例如从usersatom中选择一个随机用户并向其提问 问题 上面的数字2经常询问刚刚离开的用户
(defonce users(atom{}))
我最初选择这个引用类型是因为它的简单性,到目前为止它运行良好,但这可能需要改变
EnterMessage
和LeaveMessage
事件。我的机器人通过从CampfireAPI获取当前用户列表,然后调用reset使用新列表在用户
atom上执行code>
users
atom中选择一个随机用户并向其提问上面的数字2经常询问刚刚离开的用户或从未询问刚输入的用户,因为
用户
atom还没有被!重置
。我想我需要使用一个ref
,但是说“作家永远不会阻止通勤者或读者。”问题是,我希望那个作家阻止我的读者,对吧 ref
s主要用于协调访问多个数据结构,如果您只有一个身份(用户列表),那么refs的主要优势对您来说并不是真正的优势,尽管它也不是真正的问题。REF也有成本,因为事务可能会运行多次,所以如果您的操作有副作用,如发送消息,则事务重试时可能会发送两次消息。您可以通过使用ref和代理来解决这个问题,因为从事务发送到代理的消息保证只发送一次,并且只发送最终提交的值
在您的情况下,您可以通过以下方式做好:
- 继续使用原子
- 使用诸如
之类的符号以增量方式构建用户列表(swap!users assoc name user)
- 在atom上使用a来处理状态更改,因为手表将捕获每个状态更改
- 切换到ref
- 使用代理将实际消息发送给用户
- 使用手表可以使这更简单,但不是必需的
“作家永远不会阻挡通勤者或读者”这句话可能与你没有直接关系,但值得解释一下。对于单个ref,只读取ref值的线程从不等待,它获取当前值并继续。当有多个ref时,它可以在一个事务中读取这两个ref,并保证它将从这两个ref中获得一组一致的值,或者将重新运行,直到获得一组一致的值。需要更新值的线程如果没有得到一组一致的值,同样需要重新运行,除非它们使用
communite
函数来更新这些值,在这种情况下,STM将知道提交新值是安全的,即使其他线程也这样做(或者也可以交换)操作到值。感谢您的详细解释,这让事情变得更清楚了。我认为我将对现有的atom采用watch
方法。