如何在Haskell中轮询文件、套接字或句柄以变为可读/可写?

10

我该如何在Haskell中观察多个文件/套接字并等待其可读/可写?

Haskell中是否有类似select / epoll / ... 的功能?或者我必须为每个文件/套接字生成一个线程,并始终在该线程内使用阻塞资源?

2个回答

16
问题出在这里:你不必强制每个文件/套接字生成一个线程并使用阻塞调用,你可以生成一个线程并使用阻塞调用。这是最清晰的解决方案(在任何语言中);避免在其他语言中使用它的唯一原因是在其他语言中有点低效。 GHC的线程足够便宜,因此在Haskell中使用它并不低效。(另外,在幕后,GHC的IO管理器使用类似epoll的方式适时唤醒线程。)

谢谢澄清 :-) 我也在考虑像 Erlang 这样的语言,你可以生成一个轻量级进程,在同一进程中从多个套接字接收数据是非常常见的。 - Ricardo
1
那么如何在文件描述符上阻塞直到发生某些事情呢?仅仅说“线程”并不能真正回答这个问题。 - pdxleif
1
使用例如hGetCharhPutChar - Daniel Wagner
那种方法有点可行,但我只想在描述符上收到通知,而不是实际从中读取数据。如果我使用hGetChar从中检索数据,当我随后运行实际应该读取数据的函数时,会出现异常“资源耗尽(资源暂时不可用)”。 - pdxleif
1
原来在Control.Concurrent里有threadWaitReadthreadWaitWrite,可以阻塞当前线程,直到文件描述符准备好读取/写入,而不实际进行读取或写入。 - pdxleif

4

有一个 select(2) 的包装器:https://hackage.haskell.org/package/select
这里有一个使用示例:https://github.com/pxqr/udev/blob/master/examples/monitor.hs#L36

还有一个 poll(2) 的包装器:https://hackage.haskell.org/package/poll

GHC base 中包含了在 Linux 上(以及其他平台上)封装 epoll 的功能,位于 GHC.Event 模块中。
这里有一个使用示例:

import GHC.Event
import Data.Maybe (fromMaybe)
import Control.Concurrent (threadDelay)

main = do
  fd <- getSomeFileDescriptorOfInterest
  mgr <- fromMaybe (error "Must be compiled with -threaded") <$> getSystemEventManager
  registerFd mgr (\fdkey event -> print event) fd evtRead OneShot
  threadDelay 100000000

更多文档请参见http://hackage.haskell.org/package/base-4.11.1.0/docs/GHC-Event.html
旧版本的库示例使用请参见https://wiki.haskell.org/Simple_Servers#Epoll-based_event_callbacks。然而,该示例中的loop已被移至隐藏模块GHC.Event.Manager,并且我无法确定它是否公开导出。而GHC.Event本身表示:“此模块应视为GHC内部模块。”。
Control.Concurrent中有threadWaitReadthreadWaitWrite。因此,要翻译上面的epoll示例:
import Control.Concurrent (threadWaitRead)

main = do
  fd <- getSomeFileDescriptorOfInterest
  threadWaitRead fd
  putStrLn "Got a read ready event"

你可以使用Control.Monad.foreverthreadWaitRead和随后的IO操作包装起来,以便重复执行它们。你还可以使用forkIO将其包装在后台运行,而不妨碍程序做其他事情。

原来在 Control.Concurrent 中有 threadWaitReadthreadWaitWrite,可以阻塞当前线程直到文件描述符准备好读取/写入,而不实际进行读取或写入。 - pdxleif
"select"是POSIX函数,是否可以在Windows中使用这个包“select”?Haskell基于MinGW,所以我希望它能够工作,但我不确定... - RandomB
我会直接使用 Control.Concurrent.threadWaitRead - 这已经是 GHC 的一部分,应该是跨平台的。我不建议使用那个 select 库。 - pdxleif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接