我一直在尝试理解并发,并且一直在尝试找出更好的方法,一个大IORef
锁或多个TVar
s。我已经得出以下指导方针,我们将不胜感激,关于这些是否大致正确或我是否错过了重点。
让我们假设我们的并发数据结构是一个映射m
,像 一样访问m[i]
。还可以说我们有两个函数,f_easy
并且f_hard
. f_easy
速度很快,需要f_hard
很长时间。我们将假设 的参数f_easy/f_hard
是 的元素m
。
(1) 如果您的交易大致如下所示m[f_easy(...)] = f_hard(...)
,请使用IORef
with atomicModifyIORef
。懒惰将确保m
它只被锁定很短的时间,因为它是用一个 thunk 更新的。计算索引有效地锁定了结构(因为有些东西会被更新,但我们还不知道是什么),但是一旦知道那个元素是什么,整个结构上的 thunk 就会移动到仅在那个特定元素上的 thunk ,然后只有那个特定的元素被“锁定”。
(2) 如果你的交易看起来大致是这样m[f_hard(...)] = f_easy(...)
的,并且不要冲突太多,使用很多TVar
s。在这种情况下使用 anIORef
将有效地使应用程序成为单线程,因为您不能同时计算两个索引(因为整个结构上会有一个未解决的 thunk)。TVar
s 让你同时计算出两个索引,但是,不利的是,如果两个并发事务都访问同一个元素,并且其中一个是写入,则必须废弃一个事务,这会浪费时间(本来可以在别处使用)。如果这种情况经常发生,你可能会更好地使用来自 (通过黑洞)的锁IORef
,但如果它不经常发生,你将获得更好的TVar
s 并行性。
基本上在情况(2)中,IORef
您可能会获得 100% 的效率(没有浪费的工作)但只使用 1.1 个线程,但TVar
如果您的冲突数量较少,您可能会获得 80% 的效率但使用 10 个线程,所以您仍然结束即使浪费了工作,速度也提高了 7 倍。