在
中触发火警行动是否安全(addHandler, fire) <- newAddHandler
来自编译反应香蕉图的不同线程?
答案 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
两次。然后,
Time -> a
并像往常一样列出[(Time,a)]
。reactimate
的IO操作可能会交错。换句话说,纯FRP部分将保持纯粹,但实际的IO会像往常一样受到并发。答案 1 :(得分:3)
点击fire
处理程序本身是安全的;它读取正在原子更新的IORef
并运行当前线程中的每个添加的处理程序。这是否安全将取决于addHandler
添加了哪些处理程序。
使用addHandler
,interpretAsHandler
或fromAddHandler
中的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() {}
}