线程安全锁定条带在Haskell中的可变对象存储

时间:2013-07-20 14:35:35

标签: haskell

我使用简单的API实现了一个线程安全的可变对象存储。直观地说,它具有性能限制,即与多个并发写入器的锁争用。首先,这是API:

new :: String -> IO Int
get :: Int -> IO (Maybe String)
delete :: Int -> IO ()

实施中隐藏的是全局可访问的IORef,其中包含Ref的纯数据结构unsafePerformIO。它是IntMap,并且是增加的插槽号。将元素添加到IntMap时,插槽将递增并作为对条目的引用返回。以下是实施:

module MyModule (new,get,delete) where

import Control.Monad (liftM)
import Data.IORef
import qualified Data.IntMap as Map
import Data.Functor ((<$>))
import Data.Maybe
import System.IO.Unsafe (unsafePerformIO)

new :: String -> IO Int
new = atomicModifyIORef ref . createEntry

get :: Int -> IO (Maybe String)
get i = liftM (Map.lookup i) (table <$> readIORef ref)

delete :: Int -> IO ()
delete = atomicModifyIORef ref . deleteEntry

---------------
-- IORef mutation

data Ref = Ref { lastSlot :: !Int , table :: Map.IntMap String }

{-# NOINLINE ref #-}
ref :: IORef Ref
ref = unsafePerformIO $
        newIORef Ref { lastSlot = 0, table = Map.empty }

createEntry :: String -> Ref -> (Ref, Int)
createEntry val reg =
  ref `seq` (reg', newSlot) where
    newSlot = lastSlot reg + 1
    reg' = reg { lastSlot = newSlot,
                 table    = Map.insert newSlot val (table reg) }

deleteEntry :: Int -> Ref -> (Ref, ())
deleteEntry slot reg = (reg { table = Map.delete slot (table reg) }, ())

使用的一个例子是:

test :: IO ()
test = do
    x <- new "foo"
    y <- new "bar"
    fromJust <$> get y >>= print -- prints "bar"
    fromJust <$> get x >>= print -- prints "foo"
    delete x >> delete y

这是线程安全的,例如new可以在单独的线程中调用。这很有效。正如 Gregory Collins 所指出的,问题是争用。当多个并发写入器的情况下添加了100k +条目时,atomicModifyIORef会将值与thunk交换出来。然后,当我尝试使用IntMap下面的IORef时,多个mutators将尝试强制将值传递给WHNF,导致重复工作或线程阻塞在“黑洞”上,这两种方式都不好

我正在寻找的是另一种实现方式,newgetdelete的类型不受影响。一种可能性是使用锁定条带,以减少锁争用。 snap框架中的This HashMap implementation通过将密钥或散列空间划分为N个分区(即Vector (MVar (HashTable k v)))来实现锁定条带化,然后使用互斥锁保护每个分区。我不清楚如何使用此HashMap在我的模块中重新实现newgetdelete,而无需修改类型(这会破坏许多使用的模块)它)。

  • 我可以使用任何线程安全的Haskell并发对象存储库或HashMap库来重新实现我的API吗?
  • 我想删除unsafePerformIO
  • 我非常希望留在我的API的IO Monad中。

0 个答案:

没有答案