Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell中可变对象存储上的线程安全锁条带化_Haskell - Fatal编程技术网

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

Haskell中可变对象存储上的线程安全锁条带化,haskell,Haskell,我用一个简单的API实现了一个线程安全的可变对象存储。直观地说,它有性能限制,即与多个并发编写器的锁争用。但首先,这里是API: new :: String -> IO Int get :: Int -> IO (Maybe String) delete :: Int -> IO () 在实现中隐藏了一个全局可访问的IORef,它包含一个纯数据结构Ref,并以unsafePerformIO公开。它是一个IntMap,是一个递增的插槽号。当一个元素被添加到IntMap中时,插槽

我用一个简单的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) }, ())
modulemymodule(新建、获取、删除)其中
进口管制.Monad(liftM)
导入数据.IORef
将限定的Data.IntMap作为映射导入
导入数据。函子(())
导入数据,也许吧
导入System.IO.Unsafe(unsafePerformIO)
新建::字符串->IO Int
新建=原子修改IOREF ref。createEntry
get::Int->IO(可能是字符串)
get i=liftM(Map.lookup i)(表格readIORef ref)
删除::Int->IO()
delete=ATOMICMODIFYOREF ref。删除条目
---------------
--IORef突变
data Ref=Ref{lastSlot::!Int,table::Map.IntMap String}
{-#NOINLINE ref}
ref::IORef ref
ref=不安全性能$
newIORef Ref{lastSlot=0,table=Map.empty}
createEntry::String->Ref->(Ref,Int)
createEntry val注册表=
参考'seq`(reg',新闻地段)其中
newSlot=lastSlot reg+1
reg'=reg{lastSlot=newSlot,
table=Map.insert新闻批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
测试::IO() 测试=do x=打印--打印“条” fromJust get x>>=打印--打印“foo” 删除x>>删除y 这是线程安全的,例如可以在单独的线程中调用
new
。这是有效的。正如格雷戈里·柯林斯所指出的,问题在于争论。当在多个并发编写器案例中添加了100k+个条目时,
atomicModifyIORef
将用thunk键交换该值。然后,当我尝试使用
IORef
下面的
IntMap
时,多个变异子将尝试将值强制为WHNF,要么导致重复工作,要么线程阻塞在“黑洞”上,这两种方式都不好

我要寻找的是另一种实现,使
new
get
delete
的类型不受影响。一种可能性是使用锁条带化,以减少锁争用。在snap框架中,通过将密钥或哈希空间划分为N个分区(即
向量(MVar(哈希表k v))
)来实现锁条化,然后用互斥锁保护每个分区。我不清楚如何使用这个HashMap在我的模块中重新实现
new
get
delete
,而不修改类型(这会破坏许多使用它的模块)

  • 是否有线程安全的Haskell并发对象存储库或HashMap库可用于重新实现API
  • 我想删除
    unsafePerformIO
  • 对于我的API,我更愿意呆在IO Monad中

您看过
Control.Concurrent.STM
了吗?您应该在
ref
上使用
NOINLINE
pragma,否则编译器可能会导致一些非常奇怪的行为。有关
unsafePerformIO
的可能替代方案,您可以查看,尽管有时
unsafePerformIO
只是最好的选择。@Laar谢谢。事实上,我的实现确实使用了
NOINLINE
,尽管我并没有在整个系统中复制它。我已经编辑了上面的代码以包含它。回复:
unsafePerformIO
,我的问题仍然是:我希望在我的
IntMap
访问和转换中使用锁条带。如果所有存储桶都位于全局
IORef
,我不确定在可变存储桶之间是否会丢失条带化键空间。如果您的问题是
IntMap
,请尝试导入
Data.IntMap.Strict
,并在
Ref
中向
字段添加严格性。