【问题标题】:Message passing concurrency in Haskell with multiple senders and receiversHaskell 中具有多个发送者和接收者的消息传递并发
【发布时间】:2017-05-07 19:14:38
【问题描述】:

我正在尝试解决一个涉及多个发送方和接收方的问题,并希望得到一些反馈,说明我的方法是否走在正确的轨道上。

问题:我们有 N 个领导者和 M 个追随者,他们都必须由单独的线程代表。每个人都是舞者,并有一张相关的“舞蹈卡”,上面有 8 种不同舞蹈的名称。每个领导者都必须询问追随者是否可以跳特定的舞蹈。追随者等待领导者的邀请,并且只有在他们还没有跳那支舞并且他们还没有同意与该领导者一起跳另外 2 支舞蹈时才接受。如果领导者听到他们的邀请被接受了,他们就会继续努力为下一场舞会争取一场比赛。否则,他们会继续尝试为同一支舞寻找匹配。最后,领导者的“舞卡”上印有每支舞蹈以及与他们一起跳舞的追随者的ID。

方法:我创建了两个函数:领导者和追随者。我主要使用 forkIO 调用领导者 n 次和追随者 m 次。但是,我遇到了如何保持状态的问题(特别是舞蹈卡)。我正在考虑创建一个类型类“Dancer”,然后是它的两个实例:Leader 和 Follower。每个领导者和每个追随者都有一个唯一的 ID(从 1 到 N 或 M)。每个人还需要一个 mvar 作为自己的个人邮箱。领导者需要以某种方式“获取”一个追随者的 mvar,以便在其中放入一些东西,以便同一个追随者可以取出它并对邀请做出是或否的回应。关于舞蹈卡,我认为最好将状态单子结合起来。例如,当领导者邀请追随者参加舞会时,追随者应该能够查看他们的舞卡并确认他们还没有参加该舞会的舞伴。

【问题讨论】:

  • 根据您的描述,您已经编写了一些代码来尝试解决此问题,因此您应该包含该代码 - 它会比一堵墙更能解释您希望完成的事情文本。
  • 我也在解决这个问题。代码可以分享一下吗?

标签: haskell concurrency monads state-monad


【解决方案1】:

哇,您已经有了一个类型类、两个实例和一个状态单子,而您甚至还没有确定 MVar 的类型!事情变得复杂了。

我担心你可能会落入 Haskell-as-Java 陷阱,在这个陷阱中,你已经在脑海中想出了一个面向对象的解决方案,而你现在正试图将它直接翻译成 Haskell,思考你的舞者作为有状态的对象,共享方法包含在“类”等中。

我会建议一种不同的方法。舞者不是“东西”;他们是任务。将它们实现为简单的函数,并使用参数传递和递归来代替“状态”,这对于惯用的 Haskell 来说是典型的。

剧透如下,但这里有一个简单的方法来定义一个“follower”,它有一个id,通过一对请求/响应 MVar 响应请求,并使用以下方法维护一个舞卡递归核心循环。请注意,Follower 数据类型不应该是“追随者对象”(例如,它没有舞蹈卡);这只是记录来自follower 的返回值的一种便捷方式,它用作识别和与追随者任务通信的“句柄”:

type LeaderId = Int
type FollowerId = Int
type Dance = Int

-- |A dance card for a follower with a list of dance/leader pairs.
data Card = Card { getCard :: [(Dance, LeaderId)] } deriving (Show)
emptyCard = Card []

-- |Follower handle giving its id and request/response MVars
data Follower =
  Follower { followerId :: FollowerId
           , request :: MVar (Dance, LeaderId)
           , response :: MVar Bool
           }

-- |Create a new follower task with given id.
follower :: FollowerId -> IO Follower
follower followerId_ = do
  req <- newEmptyMVar
  res <- newEmptyMVar
  let loop (Card xs) = do
        -- get next request
        (dance, leaderId_) <- takeMVar req
        case lookup dance xs of
          -- if dance is free and we haven't danced too often w/ this leader
          Nothing | length (filter ((==leaderId_) . snd) xs) < 2
                    -- then say yes and update dance card
                    -> do putMVar res True
                          loop (Card $ (dance, leaderId_) : xs)
          -- otherwise, refuse
          _         -> do putMVar res False
                          loop (Card xs)
  forkIO $ loop emptyCard
  return $ Follower followerId_ req res

您可以通过邀请他们跳舞来创建和测试几个追随者:

> f1 <- follower 1   -- follower #1
> f2 <- follower 2   -- follower #2
> putMVar (request f1) (1, 10) -- dance #1 w/ leader #10
> takeMVar (response f1)
True                           -- hooray!
> putMVar (request f1) (1, 14) -- dance #1 w/ leader #14
> takeMVar (response f1)
False                          -- wah! dance is taken
> putMVar (request f2) (1, 14) -- try different follower
> takeMVar (response f2)
True                           -- hooray!
>

请注意,这些特定的追随者既不能被查询他们的舞蹈卡,也不能被告知退出他们的无限循环。对于这个应用程序你不需要它(我们只需要领导者的舞卡,而不关心当我们得到答案时是否有一堆卡住的轻量级线程),但你总是可以添加几个 MVar,如果你做到了。

同样,您应该能够将领导者实现为具有简单递归核心循环的函数。请注意,如果领导者试图按顺序填写其舞蹈卡,它实际上不需要跟踪舞蹈卡——最终的舞蹈卡(和“核心循环”)只是一个@987654326 @over 试图填补 1 到 8 的舞蹈位置。

您如何为领导者提供请求追随者跳舞的能力?好吧,首先创建完整的追随者集,并将追随者句柄列表 ([Follower]) 作为参数传递给 leader 创建函数。你如何从领导那里拿回舞蹈卡? leader 函数应返回卡片的 MVar,main 函数可以通过 mapM takeMVar leadersDanceCards 获取舞蹈卡片的完整列表。

【讨论】:

    猜你喜欢
    • 2014-09-29
    • 2015-07-28
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    • 2013-08-13
    • 2014-10-12
    • 1970-01-01
    相关资源
    最近更新 更多