就实际的低级原子指令和内存栅栏而言(我假设它们已被使用),您如何实现STM?
对我来说神秘的部分是,给定一些任意代码块,您需要一种方法可以在之后返回并确定每个步骤中使用的值是否有效。你是怎么做到的,你如何有效地做到这一点?这也似乎表明,就像任何其他“锁定”解决方案一样,您希望保持关键部分尽可能小(以减少冲突的可能性),我是对的吗?
此外,STM可以简单地检测“在计算执行时另一个线程进入该区域,因此计算无效”或者它是否可以实际检测是否使用了破坏的值(因此运气时有时两个线程可能执行相同的关键部分同时不需要回滚)?
答案 0 :(得分:8)
最简单的答案是“它取决于”。有很多完全不同的实现工作几乎可以想象的任何方式。
对我来说神秘的部分是,给定一些任意代码块,您需要一种方法可以在之后返回并确定每个步骤中使用的值是否有效。你是如何做到这一点的,你如何有效地做到这一点?
一种解决方案是使用版本控制。每次修改对象时,都会更新其版本号。在事务运行时,您将验证每个访问对象的版本,并在事务提交时验证对象仍然有效。
此验证可以是简单的整数比较(如果transaction_start_version >= object_version
,对象有效),因此可以相当有效地完成。
这也似乎表明,就像任何其他“锁定”解决方案一样,您希望保持关键部分尽可能小(以减少冲突的可能性),我是对的吗?
非常可能。我认为一些实现已经走了假设/要求所有成为事务的路线,但是,在大多数实现中,事务是特别标记的代码块,事务运行的时间越长,越大冲突可能导致交易回滚的可能性。
此外,STM可以简单地检测“在计算执行时另一个线程进入该区域,因此计算无效”或者它是否可以实际检测是否使用了破坏的值(因此运气时有时两个线程可能执行相同的关键部分同时不需要回滚)?
后者。请记住,TM中的想法是保护数据,而不是代码。
不同的代码路径可以访问不同事务中的相同变量。这必须由TM系统检测。没有“这个区域”的真实概念,因为它指的是代码而不是数据。 TM系统不关心正在执行什么代码,它跟踪正在修改的数据。这样,它完全不同于关键部分(保护代码而不是数据)
答案 1 :(得分:6)
GHC的STM实现在第六部分中描述:
Composable Memory Transactions。 Tim Harris,Simon Marlow,Simon Peyton Jones,Maurice Herlihy。 PPoPP'05:ACM SIGPLAN并行编程原理与实践研讨会,2005年6月,伊利诺伊州芝加哥
第五节:
Transactional memory with data invariants。蒂姆哈里斯,西蒙佩顿琼斯。 2006年3月TRANSACT '06
答案 2 :(得分:4)
我建议您观看此演示文稿:http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey
在下半部分,它解释了如何在不将值保留在未定义状态的情况下更新值。例如 - 如果您想要以STM样式更新树,则根本不会更改以前的版本。假设tree
是指向树根的指针。您创建的唯一内容是更改的节点(但它们可以引用树的原始快照中的节点。
然后对tree
指针进行比较和交换。如果成功,那么每个人现在都会看到你的新树,而旧的树可以被垃圾收集。如果没有,那么你重复这个过程,你刚刚构建的树被垃圾收集。
最大的想法是,在实际交换新旧值之前,您不需要检测是否有其他人更改了tree
,因此没有典型的“冲突”或“破坏的值”多线程编程。
答案 3 :(得分:2)