我有一个长时间运行的过程forkIO
'd,它产生像素颜色值:
takesAgesToRun :: [[Color]]
myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
writeIORef ref colors
Run Code Online (Sandbox Code Playgroud)
(其中Color
只包含三个Double
值).
正如预期的那样,当读取"另一侧"时IORef
,存储的值只是一个thunk,因此会阻止主进程.
我知道我需要完全评估[[Color]]
正常形式的值,但似乎有两种方法可以实现这一点,而且,我不确定如何将其合并到我的代码中.
我该怎么做?我是否使用rnf
,deepSeq
或其他一些线程策略?这些是其中一个首选,其他人是否已被弃用?它如何适合我的代码?
(PS请忽略这样一个事实,即将图像存储为颜色列表列表是愚蠢的 - 这只是代码的简化版本).
想知道如何最好地将Control.Lens包与IORef
s 结合起来.具体来说,我希望能够使用atomicModifyIORef
镜头,以便我可以提供类型的功能,a -> (a, b)
并从操作返回一个值.代码段:
let inc x = (x+1, x)
ior <- newIORef ((1, 1) :: (Int, Int))
thisShouldBe1 <- ior & atomicModifyIORef ?? _1 inc -- this is the bit I'm stuck on
Run Code Online (Sandbox Code Playgroud) 为了测量那些Refs的性能,我将GHC生成的组件转储到以下代码中:
import Data.IORef
main = do
r <- newIORef 18
v <- readIORef r
print v
Run Code Online (Sandbox Code Playgroud)
我期望IORef完全被优化掉,只留下一个系统调用来写字符串"18"的stdout.相反,我得到250行组装.你知道有多少人会被执行吗?以下是我认为该计划的核心内容:
.globl Main.main1_info
Main.main1_info:
_c1Zi:
leaq -8(%rbp),%rax
cmpq %r15,%rax
jb _c1Zj
_c1Zk:
movq $block_c1Z9_info,-8(%rbp)
movl $Main.main2_closure+1,%ebx
addq $-8,%rbp
jmp stg_newMutVar#
_c1Zn:
movq $24,904(%r13)
jmp stg_gc_unpt_r1
.align 8
.long S1Zo_srt-(block_c1Z9_info)+0
.long 0
.quad 0
.quad 30064771104
block_c1Z9_info:
_c1Z9:
addq $24,%r12
cmpq 856(%r13),%r12
ja _c1Zn
_c1Zm:
movq 8(%rbx),%rax
movq $sat_s1Z2_info,-16(%r12)
movq %rax,(%r12)
movl $GHC.Types.True_closure+2,%edi
leaq -16(%r12),%rsi
movl $GHC.IO.Handle.FD.stdout_closure,%r14d
addq $8,%rbp
jmp GHC.IO.Handle.Text.hPutStr2_info
_c1Zj:
movl $Main.main1_closure,%ebx
jmp …
Run Code Online (Sandbox Code Playgroud) 我正在努力了解如何IORefs
真正使用,并且我无法按照我在https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html上找到的示例代码进行操作
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
Run Code Online (Sandbox Code Playgroud)
当printCounts
执行" c <- newCounter
"时,为什么不能c
在newCounter
" return $ do
"块中得到完成工作的结果,这似乎应该IO 0
在第一次被调用时被分配给常量" "然后永远不会改变?相反,c
似乎被赋予了在" return $ do
"块中定义的函数,然后每次printCounts
到达另一个" print =<< c …
有代码读取IORef并根据某些条件和计算创建一个新值.现在它将新值写入IORef.但它有可能根本没有改变.新值可能与旧值相同.
关于在编写IORef之前是否检查值是否不同,或者只是编写IORef,需要考虑哪些因素?
writeIORef在设置之前检查值是否已更改?
通过先检查,你可以避免写入并节省一点性能吗?
我有一个写入Map和PSQ的主线程.在Map和PSQ中,我使用相同的密钥,以便通过查看PSQ,可以找到具有最小优先级的条目,其具有O(1)复杂度并且被映射到Map中的值.
现在,当我的主线程在需要时添加/修改Map和PSQ时,我有第二个线程,它经常(forever $ do
)查看PSQ以确定最旧的键何时是N ms之前然后应该刷新它.
为此,两个线程都需要查看相同的可变数据.维持国家的最佳方式是什么?这是IOREfs的案例吗?还有什么方法可以解决这个问题?
这里的"一些"pre-alpha代码:
import Data.Time
import Data.Functor
import Data.Time.Clock.POSIX
import qualified Data.PSQueue as PSQ
import qualified Data.Map as Map
import Data.Maybe
import Control.Concurrent
import Control.Concurrent.MVar
import Control.Monad
import Network.Socket hiding (send, sendTo, recv, recvFrom)
import Network.Socket.ByteString
import qualified Data.ByteString.Char8 as B
--PSQ = (host, PID) POSIXTime
--where the tuple is k and POSIXTime is p
--Map is (host, PortNumber) [messages]
--where the tuple is the key and [messages] is a list of messages
key …
Run Code Online (Sandbox Code Playgroud) 通过坚持而IORef
不是尝试使用State Monad 来维持状态似乎要容易得多.下面我们有2个可供选择的State Monads.一个使用StateT
,另一个使用ReaderT IORef
.该ReaderT IORef
可以轻松地运行在一个已知状态的最终处理.
{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables #-}
import Control.Monad.State (MonadState, execStateT, modify, StateT)
import Control.Applicative (Applicative)
import Control.Monad (void)
import Control.Monad.IO.Class ( MonadIO, liftIO )
import Data.IORef
import Control.Exception.Base
import Control.Monad.Reader (MonadReader, runReaderT, ask, ReaderT)
type StateRef = IORef Int
newtype ReadIORef a = ReadIORef { unStIORef :: ReaderT StateRef IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadReader StateRef)
newtype St a = StM { unSt :: …
Run Code Online (Sandbox Code Playgroud) 我正在努力学习Haskell,我正在玩IORef,我试图保存并查找记录.我的代码看起来像这样(注意我在这个例子中选择了"String"作为IORef类型只是为了方便和简介,在我的实际代码中我正在使用记录.而且还忽略我使用的是Set而不是一张地图,我会改变一下):
module MyTest where
import Data.IORef
import Data.Set
import Data.Foldable (find)
type State = (Set String)
type IORefState = IORef State
saveStringToState :: IO IORefState -> String -> IO String
saveStringToState stateIO string = do
state <- stateIO
atomicModifyIORef
state
(\oldStrings ->
let updatedStrings = insert string oldStrings
in (updatedStrings, updatedStrings))
stringsState <- readIORef state :: IO State
putStrLn ("### saved: " ++ show stringsState)
return string
findStringInState :: IO IORefState -> String -> IO (Maybe String)
findStringInState …
Run Code Online (Sandbox Code Playgroud) 该代码的作用是什么?是someMap
(的::Data.Map.Strict.Map
)对象的副本是否由myMap
引用或仅作为引用?我的意思是,在我阅读后可以someMap
更改(通过另一个线程)readIORef
吗?像C的易失性...可能吗?我希望它是复制/快照,因此任何更改都不会影响我someMap
或...?
do
....
someMap <- readIORef myMap
....
Run Code Online (Sandbox Code Playgroud) 我一直在问Haskell中关于并发性的几个问题,特别是TVar
我对Livelock的问题有所顾虑TVar
.
相反,我提出了这个解决方案.
(1)将程序中的所有共享数据包装在一个数据结构中,并将其包装在一个数据结构中IORef
.(2)只需使用即可进行任何更改atomicModifyIORef
.
我相信这可以防止死锁和活锁(而TVar只会阻止前者).此外,因为atomicModifyIORef
简单地将另一个thunk链接到一个链(这是一对指针操作),这不是一个瓶颈.对数据的所有实际操作可以并行完成,只要它们不相互依赖.Haskell运行时系统将解决这个问题.
但是我觉得这太简单了.我错过了什么"陷阱"吗?
这是我之前提出的问题的后续行动.如果方式更新列表中,我想IORef
在下面接受的解决方案是O(1)
或不是,在每一个电话fetch
.我怀疑这是因为IORef
可能只是保持指向列表头部的指针(而不是遍历和复制整个列表,每次都是O(n).只需将指针更改为新头应为O(1),并且应该防止急切评估整个列表).但是,ghc-core
不会显示低级代码.所以,问这里:
mklstream :: L.ByteString -> (IO S.ByteString -> IO r) -> IO r
mklstream lbs sink = do
ref <- newIORef (L.toChunks lbs)
let fetch :: IO S.ByteString
fetch = do chunks <- readIORef ref
case chunks of
[] -> return S.empty
(c:cs) -> do writeIORef ref cs
return c
sink fetch
Run Code Online (Sandbox Code Playgroud) 在haskell中,我需要一个全局变量,所以我选择使用IORef插槽,这是我的计划:
memo :: IORef Int
memo = unsafePerformIO $ newRefInt 9999
evaluate ARGs s = do
v <- Right $ unsafePerformIO $ readIORef memo
val <- Right $ VInt v
return $ (val, s)
evaluate (Call funcID exp) s = do
...
Right $ writeIORef memo 100
...
Run Code Online (Sandbox Code Playgroud)
我的计划是当执行者评估"呼叫"节点时,它会将参数保存到插槽中.然后,当评估"ARGs"节点时,将读取该备忘录槽.
但无论我做什么,我只能读取9999但不能在该槽中写入新值.
甚至我试过:
memo :: IORef Int
memo = unsafePerformIO $ newRefInt 9999
evaluate ARGs s = do
Right $ writeIORef memo 100
v <- Right $ unsafePerformIO $ readIORef memo
val …
Run Code Online (Sandbox Code Playgroud) haskell ×12
ioref ×12
shared-state ×3
do-notation ×2
atomic ×1
closures ×1
concurrency ×1
haskell-lens ×1
immutability ×1
interpreter ×1
io ×1
io-monad ×1
monads ×1
performance ×1
reference ×1