-- thread A
t <- forkIO $ do
_ <- accept listener -- blocks
-- thread B
killThread t
适用于Linux(可能也适用于OS X和FreeBSD(,但不适用于Windows(尝试使用+RTS -N4 -RTS等进行线程处理(。
- 在这种情况下,终止线程
A
的正确方法是什么? - 有没有办法以特殊模式分叉线程
A
,允许在它阻塞accept
点终止? - 如果用
forkOS
而不是forkIO
分叉A
会有所帮助吗?
只有在收到错误报告提醒时,我才注意到这种异常的 Windows 行为。
有趣的问题!
你不能中断阻止外来呼叫,所以我有点惊讶你能够在 Linux 上中断线程。此外,forkOS
也无济于事——这只会让外部代码分配线程本地存储,但与阻塞行为无关。但请记住,接受可以设置为非阻止:
如果队列中不存在挂起的连接,并且套接字 未标记为非阻塞,accept(( 阻止调用方,直到 存在连接。如果套接字标记为非阻塞且否 队列中存在挂起的连接,accept(( 失败,并显示 错误 EAGAIN 或 EWILL 阻止。
这是在 Posix 系统的网络库中完成的操作。然后,这将允许中断accept
。
关于Windows的一个有趣的说明:
-- On Windows, our sockets are not put in non-blocking mode (non-blocking
-- is not supported for regular file descriptors on Windows, and it would
-- be a pain to support it only for sockets). So there are two cases:
--
-- - the threaded RTS uses safe calls for socket operations to get
-- non-blocking I/O, just like the rest of the I/O library
--
-- - with the non-threaded RTS, only some operations on sockets will be
-- non-blocking. Reads and writes go through the normal async I/O
-- system. accept() uses asyncDoProc so is non-blocking. A handful
-- of others (recvFrom, sendFd, recvFd) will block all threads - if this
-- is a problem, -threaded is the workaround.
现在,在 Windows 上使用 -threaded 运行时接受,使用 accept_safe(这允许其他线程取得进展(——但它不会将套接字置于非阻塞模式:
accept sock@(MkSocket s family stype protocol status) = do
currentStatus <- readMVar status
okay <- sIsAcceptable sock
if not okay
then
ioError (userError ("accept: can't perform accept on socket (" ++ (show (family,stype,protocol)) ++") in status " ++
show currentStatus))
else do
let sz = sizeOfSockAddrByFamily family
allocaBytes sz $ sockaddr -> do
#if defined(mingw32_HOST_OS) && defined(__GLASGOW_HASKELL__)
new_sock <-
if threaded
then with (fromIntegral sz) $ ptr_len ->
throwErrnoIfMinus1Retry "Network.Socket.accept" $
c_accept_safe s sockaddr ptr_len
else do
paramData <- c_newAcceptParams s (fromIntegral sz) sockaddr
rc <- asyncDoProc c_acceptDoProc paramData
new_sock <- c_acceptNewSock paramData
c_free paramData
when (rc /= 0)
(ioError (errnoToIOError "Network.Socket.accept" (Errno (fromIntegral rc)) Nothing Nothing))
return new_sock
自 2005 年以来,network
包的版本,在带有 -threaded 的 Windows 上显式使用标记为 safe
的接受调用,允许其他线程取得进展,但不会将套接字本身设置为非阻塞模式(因此调用线程阻塞(。
要解决此问题,我看到两个选项:
- 研究如何在Windows上进行非阻塞接受调用,并修补网络库 - 看看例如snap或yesod在这里做了什么,看看他们是否已经解决了它。
- 使用某种监督线程来伪造 epoll,监控被阻止的子线程的进度。