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