在STM上阅读Bartosz Milewski出色的blog post时,我很高兴阅读以下内容:



但是,据我了解,这种行为不是自动的,是吗?如果我使用TVar (Map k a),它将不会充当整个 map 的单个全局锁?为了获得这种细粒度行为的好处,我(或某人)将必须实现内部包含TMap的 map 替换(例如TVars),对吗?

这似乎是一个显而易见的问题,但是在阅读STM实现时,我对TVar的读取和内存位置的读取感到困惑。我只想确保我做对了!

Bartosz进一步说:



据我了解,与STM的区别在于,尽管STM实现实际上使用锁来实现手动锁定解决方案的方式,但锁的实际获取和释放是由运行时处理的,而不是由程序员处理的-对吗?

最佳答案

TVar是可变单元格。对于不可变的结构,没有两个线程可以来回传递修改,因此我们需要一些可变单元的概念来产生效果。特别是,我们有

writeTVar :: TVar a -> a -> STM ()

这会创建一个SMT Action 来替换可变单元格中的值。我们可以将其中一些操作排序在一起,构建一个更大,更复杂的STM操作,然后调用
atomically :: STM a -> IO a

一次原子地提交整个STM Action 。这是软件事务存储的“事务”部分:具有自己对这些可变单元的引用的其他线程将仅见证atomically执行的STM操作的全部,而没有子部分。为了实现此目的,Haskell可以使用锁定或更巧妙的方法-这仅仅是实现细节。 STM唯一让您意识到的是STM块中的 Action 可以根据需要重复运行-因此,禁止修改某些共享存储单元之外的副作用。

那么,如何实现细粒度的并发呢?轻松:我们仅提供更多可变的单元格即可用于各种线程进行同步。例如,我们可以读取至少3种不同的Map类型。
TVar (Map k v)
Map k (TVar v)
TVar (Map k (TVar v))

第一个允许并发线程同时对整个Map进行修改,以使部分更改不可见。第二个是允许对任何存储值进行更改,但要保持映射本身的结构(键的选择和存储值的选择)是不可变的,并且更改不能轻易传播到其他线程。

最终选择TVar (Map k (TVar v))是最灵活的。我们可以通过在外部Map上进行同步来对TVar进行全面修改,并且可以通过读取value-TVar并在其中的操作进行同步来对存储在 map 中的值进行更改。可用于此类树的全套可能语义是多种多样的,允许“整个Map锁定”和“单个值锁定”同时发生。

关于haskell - STM是否为现有数据结构提供细粒度的锁定?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22801131/

10-11 22:56