Clojure 可变存储类型
我正在尝试从网站上提供的 API 和文档来学习 Clojure。 我对 Clojure 中的可变存储有点不清楚,我想确保我的理解是正确的。 如果我有任何错误的想法,请告诉我。
编辑:当我收到关于其正确性的评论时,我正在更新它。
免责声明:所有这些信息都是非正式的,并且可能是错误的。 不要使用这篇文章来了解 Clojure 的工作原理。
变量 始终包含根绑定,也可能包含每线程绑定。 它们与命令式语言中的常规变量相当,不适合在线程之间共享信息。 (感谢 Arthur Ulfeldt)
引用是支持原子事务的线程之间共享的位置,原子事务可以更改单个事务中任意数量引用的状态。 事务在退出同步表达式 (dosync) 时提交,并通过 STM 魔法(回滚、队列、等待等)自动解决冲突。
代理是能够以最小开销在线程之间异步共享信息的位置通过调度独立的操作函数来改变代理的状态。 代理会立即返回,因此是非阻塞的,尽管在分派函数完成之前不会设置代理的值。
原子是可以在线程之间同步共享的位置。 它们支持不同线程之间的安全操作。
以下是我根据何时使用这些结构进行的友好总结:
- 变量就像命令式语言中的常规旧变量。 (尽可能避免)
- 原子类似于 Var,但具有线程共享安全性,允许立即读取和安全设置。 (感谢 Martin)
- 代理就像一个原子,但它不会阻塞,而是生成一个新线程来计算其值,只有在更改值的过程中才会阻塞,并且可以让其他线程知道它是分配完毕。
- 引用是在事务中将自身锁定的共享位置。 我们不需要让程序员决定每段锁定代码的竞争条件期间会发生什么,而是启动一个事务并让 Clojure 处理该事务中引用之间的所有锁定条件。
另外,一个相关的概念是函数future
。 对我来说,未来对象似乎可以被描述为同步代理,在计算完成之前根本无法访问该值。 它也可以被描述为非阻塞 Atom。 这些是对未来的准确构想吗?
I'm attempting to learn Clojure from the API and documentation available on the site. I'm a bit unclear about mutable storage in Clojure and I want to make sure my understanding is correct. Please let me know if there are any ideas that I've gotten wrong.
Edit: I'm updating this as I receive comments on its correctness.
Disclaimer: All of this information is informal and potentially wrong. Do not use this post for gaining an understanding of how Clojure works.
Vars always contain a root binding and possibly a per-thread binding. They are comparable to regular variables in imperative languages and are not suited for sharing information between threads. (thanks Arthur Ulfeldt)
Refs are locations shared between threads that support atomic transactions that can change the state of any number of refs in a single transaction. Transactions are committed upon exiting sync expressions (dosync) and conflicts are resolved automatically with STM magic (rollbacks, queues, waits, etc.)
Agents are locations that enable information to be asynchronously shared between threads with minimal overhead by dispatching independent action functions to change the agent's state. Agents are returned immediately and are therefore non-blocking, although an agent's value isn't set until a dispatched function has completed.
Atoms are locations that can be synchronously shared between threads. They support safe manipulation between different threads.
Here's my friendly summary based on when to use these structures:
- Vars are like regular old variables in imperative languages. (avoid when possible)
- Atoms are like Vars but with thread-sharing safety that allows for immediate reading and safe setting. (thanks Martin)
- An Agent is like an Atom but rather than blocking it spawns a new thread to calculate its value, only blocks if in the middle of changing a value, and can let other threads know that it's finished assigning.
- Refs are shared locations that lock themselves in transactions. Instead of making the programmer decide what happens during race conditions for every piece of locked code, we just start up a transaction and let Clojure handle all the lock conditions between the refs in that transaction.
Also, a related concept is the function future
. To me, it seems like a future object can be described as a synchronous Agent where the value can't be accessed at all until the calculation is completed. It can also be described as a non-blocking Atom. Are these accurate conceptions of future?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
听起来您真的很了解 Clojure! 干得好:)
变量有一个在所有线程中可见的“根绑定”,每个单独的线程都可以更改它看到的值,而不会影响其他线程。 如果我的理解是正确的,则 var 不能仅存在于一个线程中,而没有对所有人可见的根绑定,并且在第一次使用 (def ... ) 定义它之前,它不能“反弹”。
引用在包含更改的 (dosync ...) 事务结束时提交,但仅当事务能够以一致状态完成时才提交。
It sounds like you are really getting Clojure! good job :)
Vars have a "root binding" visible in all threads and each individual thread can change the value it sees with out affecting the other threads. If my understanding is correct a var cannot exist in just one thread with out a root binding that is visible to all and it cant be "rebound" until it has been defined with (def ... ) the first time.
Refs are committed at the end of the (dosync ... ) transaction that encloses the changes but only when the transaction was able to finish in a consistent state.
我认为你关于 Atoms 的结论是错误的:
原子通过
swap!
进行更改,或者通过compare-and-set!
进行低级更改。 这永远不会阻止任何东西。swap!
的工作方式类似于只有一个引用的事务:I think your conclusion about Atoms is wrong:
Atoms are changed with
swap!
or low-level withcompare-and-set!
. This never blocks anything.swap!
works like a transaction with just one ref:我发现你的问题有两个问题。
你说:
http://clojure. org/agents 说:
,即 您永远不必等待获取代理的值(我假设操作更改的值被代理并以原子方式更改)。
Agent
的deref
方法的代码如下所示(SVN 修订版 1382):}
不涉及阻塞。
另外,我不明白你的意思(在你的参考部分)
当 dosync 块的所有操作均已完成、未引发任何异常且没有任何情况导致重试事务时,将提交事务。 我认为 deref 与此无关,但也许我误解了你的观点。
I've found two issues with your question.
You say:
http://clojure.org/agents says:
I.e. you never have to wait to get the value of an agent (I assume the value changed by an action is proxied and changed atomically).
The code for the
deref
-method of anAgent
looks like this (SVN revision 1382):}
No blocking is involved.
Also, I don't understand what you mean (in your Ref section) by
Transactions are committed when all actions of the dosync block have been completed, no exceptions have been thrown and nothing has caused the transaction to be retried. I think
deref
has nothing to do with it, but maybe I misunderstand your point.Martin 说得对,Atoms 操作从 1 开始重新启动,直到最终成功。
也称为自旋等待。
虽然值得注意的是真正阻塞在锁上,但执行该操作的线程将被阻塞,直到操作成功,因此它是阻塞操作而不是异步操作。
另外关于 Futures,Clojure 1.1 添加了 Promise 和 futures 的抽象。
Promise 是一种同步构造,可用于将值从一个线程传递到另一个线程。 在交付值之前,任何取消引用 Promise 的尝试都将被阻止。
期货代表异步计算。 它们是一种让代码在另一个线程中运行并获取结果的方法。
Martin is right when he say that Atoms operation restarts at 1. until is succeeds eventually.
It is also called spin waiting.
While it is note really blocking on a lock the thread that did the operation is blocked until the operation succeeds so it is a blocking operation and not an asynchronously operation.
Also about Futures, Clojure 1.1 has added abstractions for promises and futures.
A promise is a synchronization construct that can be used to deliver a value from one thread to another. Until the value has been delivered, any attempt to dereference the promise will block.
Futures represent asynchronous computations. They are a way to get code to run in another thread, and obtain the result.
变量并不总是具有根绑定。 在没有绑定的情况下创建 var 是合法的,使用
或
尝试在 x 有值之前对其求值将导致
Vars don't always have a root binding. It's legal to create a var without a binding using
or
Attempting to evaluate x before it has a value will result in