软件交易内存 - 合并性示例
总是被提及的软件事务内存的主要优点之一是可组合性和模块化。不同的片段可以组合起来产生更大的组件。在基于锁的程序中,情况通常并非如此。
我正在寻找一个用实际代码来说明这一点的简单示例。我更喜欢 Clojure 中的示例,但 Haskell 也很好。如果示例还展示了一些无法轻松编写的基于锁的代码,那就加分了。
One of the major advantages of software transactional memory that always gets mentioned is composability and modularity. Different fragments can be combined to produce larger components. In lock-based programs, this is often not the case.
I am looking for a simple example illustrating this with actual code. I'd prefer an example in Clojure, but Haskell is fine too. Bonus points if the example also exhibits some lock-based code which can't be composed easily.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
Java 中锁不组合的示例:
因此,存款可以,取款可以,但转账不行:如果当 B 开始向 A 转账时 A 也开始向 B 转账,我们可能会出现死锁情况。
现在在 Haskell STM 中:
由于没有显式锁定,
withdraw
和deposit
自然地组合在transfer
中。语义仍然确保如果提款失败,则整个传输失败。它还确保提款和存款将自动完成,因为类型系统确保您无法在原子
之外调用转账。这个例子来自于:
http://cseweb.ucsd.edu/classes/ wi11/cse230/static/lec-stm-2x2.pdf
改编自这篇论文,您可能想阅读:
http://research.microsoft.com/pubs/74063/beautiful.pdf
An example where locks don't compose in Java:
So, deposit is okay, withdraw is okay, but transfer is not okay: if A begins a transfer to B when B begins a transfer to A, we can have a deadlock situation.
Now in Haskell STM:
Since there is no explicit lock,
withdraw
anddeposit
compose naturally intransfer
. The semantics still ensure that if withdraw fails, the whole transfer fails. It also ensures that the withdraw and the deposit will be done atomically, since the type system ensures that you cannot call transfer outside of anatomically
.This example comes from this:
http://cseweb.ucsd.edu/classes/wi11/cse230/static/lec-stm-2x2.pdf
Adapted from this paper you might want to read:
http://research.microsoft.com/pubs/74063/beautiful.pdf
Ptival 示例到 Clojure 的翻译:
所以
debit
和credit
可以单独调用,并且像 Haskell 版本一样,交易嵌套,所以整个传输操作是原子的,在提交冲突时会发生重试,您可以添加验证器以确保一致性等。
当然,语义是它们永远不会死锁。
A translation of Ptival's example to Clojure:
So
debit
andcredit
are fine to call on their own, and like the Haskell version, the transactions nest, so the wholetransfer
operation is atomic, retries will happen on commit collisions, you could add validators for consistency, etc.And of course, semantics are such that they will never deadlock.
这是一个 Clojure 示例:
假设您有一个银行账户向量(在现实生活中,该向量可能会更长......):
一个“转账”函数,可以在单笔交易中安全地在两个账户之间转账:
以及 如下所示:
然后,您可以轻松地组合转账功能来创建更高级别的交易,例如从多个账户转账:
请注意,所有多次转账都发生在单个组合交易中,即可以“组合”较小的交易交易。
使用锁来做到这一点很快就会变得复杂:假设需要单独锁定帐户,那么您需要做一些事情,例如建立关于锁获取顺序的协议,以避免死锁。正如乔恩正确指出的那样,在某些情况下,您可以通过对系统中的所有锁进行排序来做到这一点,但在大多数复杂的系统中这是不可行的。很容易犯难以察觉的错误。 STM 可以帮助您摆脱所有这些痛苦。
Here's a Clojure example:
Suppose you have a vector of bank accounts (in real life the vector may be somewhat longer....):
And a "transfer" function that safely transfers an amount between two accounts in a single transaction:
Which works as follows:
You can then easily compose the transfer function to create a higher level transaction, for example transferring from multiple accounts:
Note that all of the multiple transfers happened in a single, combined transaction, i.e. it was possible to "compose" the smaller transactions.
To do this with locks would get complicated very quickly: assuming the accounts needed to be individually locked then you'd need to do something like establishing a protocol on lock acquisition order in order to avoid deadlocks. As Jon rightly points out you can do this in some cases by sorting all the locks in the system, but in most complex systems this isn't feasible. It's very easy to make a hard-to-detect mistake. STM saves you from all this pain.
为了使 trprcolin 的示例更加惯用,我建议更改 transact 函数中的参数顺序,并使 借方 和 贷方 的定义更加紧凑。
And to make trprcolin's example even more idiomatic i would suggest changing parameter order in transact function, and make definitions of debit and credit more compact.