6

触发火灾行动是否安全

(addHandler, fire) <- newAddHandler

来自编译反应香蕉图的不同线程?

4

2 回答 2

4

是的,这是安全的,但有@Cirdec 提到的警告。

为了具体起见,请考虑以下示例,该示例在单独的线程中使用创建事件网络addHandler,然后fire在主线程中重复调用

import Control.Concurrent (myThreadId, threadDelay, forkIO)

main = do
    ...
    (addHandler, fire) <- newAddHandler

    let networkDescription :: MomentIO ()
        networkDescription = do
           e <- fromAddHandler addHandler
           ...
           reactimate $ (print =<< myThreadId) <$ e   -- reactimate

    forkIO $ do
        network <- compile networkDescription
        actuate network
    ...
    forever $ do                                      -- event loop
        threadDelay (10^6)
        fire ()

(请参阅 Control.Concurrent 中的文档“终止程序”,了解为什么我将事件循环放在主线程中,而不是将网络放在主线程中。)

在这种情况和类似情况下,以下情况将成立:

  • 执行的 IO 动作reactimate将在调用的线程中运行fire而不是在编译网络的线程中运行。这就是@Cirdec 已经提到的。
  • 如果有第二个线程也在调用fire,那么它可能会与对 的其他调用交错fire,即程序可能同时调用fire两次。然后,
    • Reactive-banana 使用锁来确保一致地更新行为和事件。您可以像往常一样将它们视为纯函数Time -> a和列表。[(Time,a)]
    • 但是,来自 s 的 IO 操作reactimate可能会交错。换句话说,纯 FRP 部分将保持纯,但实际的 IO 像往常一样受到并发的影响。
于 2015-09-17T10:03:19.240 回答
3

触发fire处理程序本身是安全的;它读取一个IORef正在自动更新的,并在当前线程中运行每个添加的处理程序。这是否安全取决于添加到addHandler.

使用addHandlerin interpretAsHandler, fromAddHandler, orfromChanges应该是安全的。我在反应香蕉中所知道的没有任何线程亲和力,即使有,这些也是newAddHandler为之而生的,所以无论如何它应该是安全的。

您需要注意的IO ()reactimate. 如果您需要对IO需要在特定线程中运行的操作(用于 OpenGL 输出等)做出反应,您只需生成IO ()将其数据发送到该线程的操作。在这个用于反应香蕉的完整 OpenGL 示例中IO (),具有线程关联性的 OpenGL 输出操作在 OpenGL 线程中运行。而不是reactimate直接Event (IO ())执行它们,而是将它们添加到IORef

whenIdleRef <- newIORef (return ())
let
    addWhenIdle :: IO () -> IO ()
    addWhenIdle y = atomicModifyIORef' whenIdleRef (\x -> (x >> y, ()))
    runWhenIdle :: IO ()
    runWhenIdle = atomicModifyIORef' whenIdleRef (\x -> (return (), x)) >>= id

let networkDescription  :: forall t. Frameworks t => Moment t ()
    networkDescription  = do

        reactimate $ fmap addWhenIdle (whenIdle outputs)
                     ^                ^
                     |                Event (IO ())
                     Stuff the event into an IORef

IORef读取要运行的IO ()动作,并且所有动作中的每一个都在我知道在 OpenGL 线程中的上下文中运行。

idleCallback $= Just (do           -- will be executed in the OpenGL thread when it's idle
    getCurrentTime >>= raiseTime
    runWhenIdle                    -- run those `IO ()` actions in this thread
    postRedisplay Nothing)
于 2015-09-16T16:44:34.993 回答