【发布时间】:2018-08-19 01:26:47
【问题描述】:
我开始使用 Haskell 进行异步编码,现在我使用 forkIO 创建一个绿色线程(对吗?是绿色线程吗?)然后我使用 MVar 进行通信一旦我完成并且我拥有价值,就从新线程到主线程。这是我的代码:
responseUsers :: ActionM ()
responseUsers = do emptyVar <- liftAndCatchIO $newEmptyMVar
liftAndCatchIO $ forkIO $ do
users <- getAllUsers
putMVar emptyVar users
users <- liftAndCatchIO $ takeMVar emptyVar
json (show users)
读完MVar 类后,我可以看到是一个块线程类,如果 MVar 为空,则阻塞线程直到被填充。
我来自Scala,在其他避免阻塞的情况下,我们在 Future 对象中有回调的概念,其中线程A 可以创建一个线程B 并接收一个Future。
然后订阅一个回调函数onComplete,一旦线程B 完成该值,它将被调用。
但在那段时间里,线程A并没有被阻塞,可以重复用于其他操作。
例如,在我们的 Http 服务器框架中,如 Vertx 或 Grizzly 通常配置为具有少量操作系统线程 (4-8),因为它们永远不会被阻塞。
我们在 Haskell 中没有另一种纯粹的无阻塞机制吗?
问候
【问题讨论】:
-
Haskell 线程非常便宜,因为运行时可以在单个操作系统线程上运行其中的许多线程。它们确实是绿线。通常,使线程阻塞是最简单和最有效的选择——无论如何,运行时将为其他绿色线程重用 OS 线程,因此不会丢失任何内容。如上所述,使用
MVars 或TVars(或任何其他并发原语)同步绿色线程,如果您需要更多并发,不要害怕多次调用forkIO(即使是短任务) . -
所以你说,例如在我的 Http 服务器实现中,当我收到请求时,不是在操作系统中运行,而是在绿色线程中运行,所以程序可以继续接收请求?如果是这样,它会自动执行。
-
因为在我的示例中,操作系统线程是创建绿色线程并阻塞直到绿色线程完成的线程
-
在上面的代码中有太多的同步。产生了一个新线程,原始线程立即等待它完成 (
takeMVar)。这是没有意义的,因为它是。相反,如果原始线程在forkIO之后和takeMVar之前做了一些事情,它可能会很有用。 -
RTS 并不真正执行阻塞系统调用——它使用 POSIX 系统中的 select/poll 之类的东西来等待 IO 并将其分派给等待线程。此外,它每隔 N 毫秒左右在绿色线程之间执行“上下文切换”。