在reactive-banana中,从多个线程触发处理程序操作是否安全?

时间:2015-09-16 14:53:20

标签: haskell frp reactive-banana

中触发火警行动是否安全
(addHandler, fire) <- newAddHandler

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

2 个答案:

答案 0 :(得分:4)

是的,这是安全的,但@Cirdec提到了一些警告。

对于conreteness,请考虑以下示例,该示例在单独的线程中使用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 ()

(请参阅文档"Terminating the program" in Control.Concurrent,了解为什么我将事件循环放在主线程中,而不是将网络放在主线程中。)

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

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

答案 1 :(得分:3)

点击fire处理程序本身是安全的;它读取正在原子更新的IORef并运行当前线程中的每个添加的处理程序。这是否安全将取决于addHandler添加了哪些处理程序。

使用addHandlerinterpretAsHandlerfromAddHandler中的fromChanges应该是安全的。我在反应香蕉中所知道的任何东西都没有任何线程亲和力,即使它确实如此,这些都是newAddHandler所做的,所以无论如何它应该是安全的。

您需要注意的是IO ()执行的reactimate操作。如果您需要重新启动需要在特定线程中运行的IO操作(对于OpenGL输出等),您只需要生成将IO ()动作发送到该线程的IO ()动作。在此complete OpenGL example for reactive-banana中,具有线程关联性的OpenGL输出的reactimate操作在OpenGL线程中运行。而不是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 ()持有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) 个要执行的操作,并且所有操作都在我知道的OpenGL线程的上下文中运行。

public class ClassName {
    private static final ClassName instance = new ClassName();

    public static ClassName getInstance() {
        return instance;
    }

    private ClassName() {}
}