jbe*_*man 8 arrays haskell ghc
我正在使用该primitive包,我想确保来自一个线程的写入(特别是比一个单词更宽泛的类型)不能被视为来自另一个线程的垃圾.这有时被称为"撕裂".
我猜这是未定义的行为,并且在任何情况下,多字写入都不是原子的,正如这个快速而肮脏的程序所证明的那样:
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
module Main where
import Data.Primitive.Types
import Data.Primitive.ByteArray
import Control.Concurrent
import Control.Concurrent.STM
import Control.Applicative
import Control.Monad
import GHC.Prim
main = do
arr <- newByteArray 16 -- on 64-bit, this is enough for two Ints (8 each)
inp <- newTVarIO (0::Int)
o1 <- newEmptyTMVarIO
o2 <- newEmptyTMVarIO
let writer last = do
val <- atomically $ do
x <- readTVar inp
x <$ check (x > last)
let v' = (val,val+1)
writeByteArray arr 0 v'
atomically $ putTMVar o1 ()
writer val
reader last = do
val <- atomically $ do
x <- readTVar inp
x <$ check (x > last)
rVal <- readByteArray arr 0 :: IO (Int,Int)
let v1 = (val,val+1)
v0 = (val-1,val)
when (not $ rVal `elem` [v0,v1,(0,0)]) $ error $ show (val, "got:", rVal)
atomically $ putTMVar o2 ()
reader val
let control :: Int -> IO ()
control !n = do
atomically $ writeTVar inp n
mapM_ (atomically . takeTMVar) [o1,o2]
when (n<100000) $ control (n+1)
forkIO $ writer 0
forkIO $ reader 0
control 1
print "done!"
instance Prim (Int,Int) where
sizeOf# _ = 2# *# (sizeOf# (undefined :: Int))
alignment# _ = alignment# ( undefined :: Int)
readByteArray# arr n s = case readByteArray# arr (2# *# n) s of
(#s',i1 #) -> case readByteArray# arr ((2# *# n) +# 1#) s of
(#s'2,i2 #) -> (#s'2,(i1,i2)#)
writeByteArray# arr n (i1,i2) s = case writeByteArray# arr (2# *# n) i1 s of
s' -> writeByteArray# arr ((2# *# n) +# 1#) i2 s'
Run Code Online (Sandbox Code Playgroud)
使用 ghc-7.6.3 构建这个程序-O2 -threaded -rtsopts,在 7 次执行中-N3我得到了以下结果:
foo: (4,"got:",(3,5))
foo: (59037,"got:",(59036,59038))
foo: "done!"
foo: (92936,"got:",(92936,92936))
foo: (399,"got:",(398,400))
foo: (7196,"got:",(7195,7197))
foo: (11950,"got:",(11950,11950))
Run Code Online (Sandbox Code Playgroud)
只要 CPU 架构的内存模型能够保证这一点,单个机器字的读/写可能是原子的。
对这个演示的一个反对意见是,Prim 实例(Int,Int)是假的。确实是这样。然而,考虑到可用的原语,我不知道如何为多字类型实现更好的东西。
您需要使用其他一些同步方法来确保多字写入是原子的。一种简单的方法是将数组保存在MVar. 或者也许我的kickchan包会有所帮助(至少是鼓舞人心的,如果它不能解决您的用例)。