Haskell中的阻塞线程

时间:2018-08-18 12:11:33

标签: haskell scotty

我开始使用Haskell进行异步编码,现在我正在使用forkIO来创建一个绿色线程(是否正确?是绿色线程吗?),然后我使用{{1 }}完成后,我就有了从新线程到主线程的通信。这是我的代码:

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,在其他情况下,我们避免阻塞,在Future对象中有回调的概念,其中线程Scala可以创建线程A并接收{ {1}}。

然后订阅回调函数B,一旦线程Future的值结束,它将被调用。

但是在这段时间内,线程onComplete没有被阻塞,可以重新用于其他操作。

例如在我们的Http服务器框架中,例如BA通常配置为具有少量OS线程(4-8),因为它们永远都不会被阻塞。

我们在Haskell中是否还有另一种完全没有阻塞的机制?

致谢

1 个答案:

答案 0 :(得分:4)

好的,这里有很多东西要解压。首先,让我们讨论您的特定代码示例。为Scotty编写responseUsers处理程序的正确方法是:

responseUsers :: ActionM ()
responseUsers = do
  users <- getAllUsers
  json (show users)

即使getAllUsers需要花一天半的时间运行,并且一百个客户端全部一次发出getAllUsers请求,其他都不会阻塞,并且您的Scotty服务器将继续处理请求。要查看此信息,请考虑以下服务器:

{-# LANGUAGE OverloadedStrings #-}

import Web.Scotty
import Control.Concurrent
import Control.Monad.IO.Class
import qualified Data.Text.Lazy as T

main = scotty 8080 $ do
  get "/fast" $ html "<h1>Fast Response</h1><p>I'm ready!"
  get "/slow" $ liftIO (threadDelay 30000000) >> html "<h1>Slow</h1><p>Whew, finally!"
  get "/pure" $ html $ "<h1>Answer</h1><p>The answer is " 
                <> (T.pack . show . sum $ [1..1000000000])

如果您对此进行编译并启动,则可以打开多个浏览器标签以进行以下操作:

http://localhost:8080/slow
http://localhost:8080/pure
http://localhost:8080/fast

,您将看到fast链接立即返回,即使slowpure链接分别在IO和纯计算上被阻塞。 (threadDelay没什么特别的-它可能是任何IO操作,例如访问数据库或读取大文件或代理到其他HTTP服务器等。)您可以继续为{{1 }},fastslow,当服务器继续接受更多请求时,速度较慢的请求将在后台消失。 (pure计算与pure计算略有不同-它只会在第一次出现时阻塞,等待它的所有线程都将立即返回一个答案,随后的请求将很快。如果我们诱使Haskell为每个请求重新计算它,或者如果它实际上依赖于请求中提供的某些信息(在更现实的服务器中可能就是这种情况),则其行为或多或少类似于slow计算)

您在这里不需要任何回调,也不需要主线程“等待”结果。 Scotty分叉来处理每个请求的线程可以执行所需的任何计算或IO活动,然后将响应直接返回给客户端,而不会影响任何其他线程。

此外,除非您使用slow编译此服务器并在编译或运行时提供线程计数> 1,否则它仅在一个OS线程中运行。 ,它会自动在单个OS线程中完成所有操作!

第二,这实际上与Scotty无关。您应该将Haskell运行时视为在OS线程机制之上提供了一个线程抽象层,并且OS线程是您不必担心的实现细节(很好,除非在特殊情况下,例如重新与需要在某些OS线程中发生某些事情的外部库进行交互。

因此,所有Haskell线程,甚至是“主”线程,都是绿色的,并且在某种虚拟机上运行,​​无论有多少绿色线程阻塞,该虚拟机都可以在单个OS线程上正常运行无论出于什么原因。

因此,编写异步请求处理程序的典型模式是:

-threaded

请注意,这里不需要回调。 loop :: IO () loop = do req <- getRequest forkIO $ handleRequest req loop 函数在每个可以执行长时间运行的纯CPU绑定计算,阻止IO操作以及其他所需操作的请求的单独绿色线程中运行,并且处理线程不需要传递结果回到主线程,以便最终处理请求。它可以直接将结果传达给客户端。

Scotty基本上是围绕这种模式构建的,因此它可以自动调度多个请求,而无需回调或阻塞OS线程。