我开始用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,在其他情况下,避免阻塞,我们在未来对象中有回调的概念,线程A可以在这里创建线程B并接收Future。
然后,订阅一个回调函数onComplete,一旦线程B使用该值完成,它将被调用。
但是在此期间,线程A不会被阻塞,并且可以被重用到另一个操作中。
例如,在我们的Http服务器框架(如Vertx或Grizzly )中,通常配置为有少量OS线程(4-8个),因为它们永远不应该被阻塞。
我们在Haskell不是有另一种纯粹的无阻塞机制吗?
问候
发布于 2018-08-18 17:26:47
好吧,这里有很多东西要打开。首先,让我们讨论您的特定代码示例。为Scotty编写responseUsers处理程序的正确方法是:
responseUsers :: ActionM ()
responseUsers = do
users <- getAllUsers
json (show users)即使getAllUsers需要一天半的时间运行,并且100个客户机都同时发出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链接立即返回,即使slow和pure链接分别在IO和纯计算中被阻塞。( threadDelay没有什么特别之处-它可能是任何IO操作,比如访问数据库、读取大文件或代理到另一个HTTP服务器或其他什么。)您可以继续为fast、slow和pure启动多个附加请求,而缓慢的请求将在后台运行,而服务器将继续接受更多的请求。( pure计算与slow计算略有不同--它只会第一次阻塞,等待它的所有线程都会立即返回一个答案,随后的请求将是快速的。如果我们诱使Haskell为每个请求重新计算它,或者它实际上依赖于请求中提供的某些信息,就像在更现实的服务器中那样,那么它的行为或多或少就像slow计算一样。)
这里不需要任何类型的回调,也不需要主线程来“等待”结果。Scotty为处理每个请求而分叉的线程可以执行所需的任何计算或IO活动,然后直接将响应返回给客户端,而不会影响任何其他线程。
此外,除非您使用-threaded编译此服务器并在编译或运行时提供线程计数>1,否则只在一个OS线程中运行。因此,默认情况下,它在一个OS线程中执行所有这些操作!
第二,这其实并没有什么特别的关于斯科蒂。您应该将Haskell运行时看作是在OS线程机制之上提供线程抽象层,而OS线程是您不必担心的实现细节(好吧,除非在不寻常的情况下,例如,如果您与外部库接口,需要在某些OS线程中发生某些事情)。
因此,所有Haskell线程,甚至是“主”线程,都是绿色的,并且运行在一种虚拟机之上,在一个OS线程之上运行得很好,不管出于什么原因有多少绿色线程阻塞。
因此,编写异步请求处理程序的典型模式是:
loop :: IO ()
loop = do
req <- getRequest
forkIO $ handleRequest req
loop请注意,这里不需要回调。handleRequest函数为每个请求运行一个单独的绿色线程,这些请求可以执行长时间运行的纯CPU绑定计算、阻塞IO操作以及其他任何需要的操作,并且处理线程不需要将结果传递回主线程以最终服务请求。它可以直接将结果传递给客户端。
Scotty基本上是围绕这种模式构建的,因此它可以自动分发多个请求,而不需要回调或阻塞OS线程。
https://stackoverflow.com/questions/51908524
复制相似问题