首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell中的阻塞线程

Haskell中的阻塞线程
EN

Stack Overflow用户
提问于 2018-08-18 12:11:33
回答 1查看 596关注 0票数 1

我开始用Haskell编写异步代码,现在我使用的是创建绿色线程的forkIO (这是正确的吗?是一根绿线吗?)然后,我使用一个MVar在完成后从新线程到主线程进行通信,并且我有这个值。这里我的代码:

代码语言:javascript
复制
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服务器框架(如VertxGrizzly )中,通常配置为有少量OS线程(4-8个),因为它们永远不应该被阻塞。

我们在Haskell不是有另一种纯粹的无阻塞机制吗?

问候

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-08-18 17:26:47

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

代码语言:javascript
复制
responseUsers :: ActionM ()
responseUsers = do
  users <- getAllUsers
  json (show users)

即使getAllUsers需要一天半的时间运行,并且100个客户机都同时发出getAllUsers请求,其他任何东西都不会阻塞,您的Scotty服务器也将继续处理请求。要查看这一点,请考虑以下服务器:

代码语言:javascript
复制
{-# 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])

如果编译并启动它,则可以打开多个浏览器选项卡:

代码语言:javascript
复制
http://localhost:8080/slow
http://localhost:8080/pure
http://localhost:8080/fast

您将看到fast链接立即返回,即使slowpure链接分别在IO和纯计算中被阻塞。( threadDelay没有什么特别之处-它可能是任何IO操作,比如访问数据库、读取大文件或代理到另一个HTTP服务器或其他什么。)您可以继续为fastslowpure启动多个附加请求,而缓慢的请求将在后台运行,而服务器将继续接受更多的请求。( pure计算与slow计算略有不同--它只会第一次阻塞,等待它的所有线程都会立即返回一个答案,随后的请求将是快速的。如果我们诱使Haskell为每个请求重新计算它,或者它实际上依赖于请求中提供的某些信息,就像在更现实的服务器中那样,那么它的行为或多或少就像slow计算一样。)

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

此外,除非您使用-threaded编译此服务器并在编译或运行时提供线程计数>1,否则只在一个OS线程中运行。因此,默认情况下,它在一个OS线程中执行所有这些操作!

第二,这其实并没有什么特别的关于斯科蒂。您应该将Haskell运行时看作是在OS线程机制之上提供线程抽象层,而OS线程是您不必担心的实现细节(好吧,除非在不寻常的情况下,例如,如果您与外部库接口,需要在某些OS线程中发生某些事情)。

因此,所有Haskell线程,甚至是“主”线程,都是绿色的,并且运行在一种虚拟机之上,在一个OS线程之上运行得很好,不管出于什么原因有多少绿色线程阻塞。

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

代码语言:javascript
复制
loop :: IO ()
loop = do
  req <- getRequest
  forkIO $ handleRequest req
  loop

请注意,这里不需要回调。handleRequest函数为每个请求运行一个单独的绿色线程,这些请求可以执行长时间运行的纯CPU绑定计算、阻塞IO操作以及其他任何需要的操作,并且处理线程不需要将结果传递回主线程以最终服务请求。它可以直接将结果传递给客户端。

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51908524

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档