epoll
我想使用“ ”风格的事件管理来实现高效的单线程套接字通信。
如果我要“从头开始”编写一个非常命令式的程序,我基本上会这样做(只是我刚刚输入的一些伪代码 - 可能无法编译):
import Control.Concurrent
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified GHC.Event as Event
import Network
import Network.Socket
import Network.Socket.ByteString
main = withSocketFromSomewhere $ \ socket -> do
let fd = fromIntegral . fdSocket $ socket
-- Some app logic
state <- newMVar "Bla"
-- Event manager
manager <- Event.new
-- Do an initial write
initialWrite socket state manager
-- Manager does its thing
Event.loop manager
write manager socket bs =
-- Should be pretty straight-forward
Event.registerFd manager theWrite fd Event.evtWrite
where
fd = fromIntegral . fdSocket $ socket
theWrite key _ = do
Event.unregisterFd manager key
sendAll socket bs
read manager socket cont =
-- Ditto
Event.registerFd manager theRead fd Event.evtRead
where
fd = fromIntegral . fdSocket $ socket
theRead key _ = do
Event.unregisterFd manager key
bs <- recv socket 4096
cont bs
initialWrite socket state manager = do
msg <- readMVar state
write manager socket msg
read manager socket $ \ bs -> do
ByteString.putStrLn bs
putMVar state msg
想象一下,还有一些函数可以向管理器添加超时事件,等等。
但是,这段代码并不是特别好,原因如下:
- 我手动携带事件管理器。
- 我必须
MVar
为我的应用程序逻辑使用一个,因为我不能告诉不透明的事件管理器它应该为我传递一些状态,即使我知道它只使用一个线程,因此可能被用作基础一个单子变压器堆栈。 - 我必须为读取创建明确的分隔延续(我什至可能必须为写入执行此操作;我不知道在这种情况下什么会更明智)。
现在,这只是为使用过多的单子变压器等而尖叫。我希望能够做到这一点:
main =
withSocketFromSomewhere $ \ socket ->
runEvents . flip runStateT "Bla" $ initialWrite socket
initialWrite socket = do
msg <- lift get
write socket msg
resp <- read socket
liftIO $ ByteString.putStrLn resp
lift $ put msg
此代码应具有与上述代码相同的行为;例如,通过暂停计算直到resp <- read socket
在线接收到读取,并让我在同一个线程/管理器上管理多个套接字。
问题:
- GHC 事件 API/libevent/equivalent 是否有更高级的接口,可以为用户提供更多功能?考虑最近 GHC 中发生的同步 IO 调度更改是否值得(我在 7.4.1 上)?
- 如果我想实现协作并发,例如拥有一个始终处理来自套接字的读取的函数,但让该函数与写入“线程”共享相同的状态单子,该怎么办?这可以用(1)中的任何解决方案来完成吗?