【问题标题】:What is wrong with the following solution to the "Dining Philosophers"?“餐饮哲学家”的以下解决方案有什么问题?
【发布时间】:2012-08-30 18:57:28
【问题描述】:

为了熟悉 Haskell 中的 STM,我写了以下解决哲学家就餐问题的方法:

import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import System.Random

type Fork = TVar Bool
type StringBuffer = TChan String

philosopherNames :: [String]
philosopherNames = map show ([1..] :: [Int])

logThinking :: String -> StringBuffer -> STM ()
logThinking name buffer = writeTChan buffer $ name ++ " is thinking..."

logEating :: String -> StringBuffer -> STM ()
logEating name buffer = writeTChan buffer $ name ++ " is eating..."

firstLogEntry :: StringBuffer -> STM String
firstLogEntry buffer = do empty <- isEmptyTChan buffer
                          if empty then retry
                                   else readTChan buffer

takeForks :: Fork -> Fork -> STM ()
takeForks left right = do leftUsed <- readTVar left
                          rightUsed <- readTVar right
                          if leftUsed || rightUsed
                             then retry
                             else do writeTVar left True
                                     writeTVar right True

putForks :: Fork -> Fork -> STM ()
putForks left right = do writeTVar left False
                         writeTVar right False

philosopher :: String -> StringBuffer -> Fork -> Fork -> IO ()
philosopher name out left right = do atomically $ logThinking name out
                                     randomDelay
                                     atomically $ takeForks left right
                                     atomically $ logEating name out
                                     randomDelay
                                     atomically $ putForks left right

randomDelay :: IO ()
randomDelay = do delay <- getStdRandom(randomR (1,3))
                 threadDelay (delay * 1000000)

main :: IO ()
main = do let n = 8
          forks <- replicateM n $ newTVarIO False
          buffer <- newTChanIO
          forM_ [0 .. n - 1] $ \i ->
              do let left = forks !! i
                     right = forks !! ((i + 1) `mod` n)
                     name = philosopherNames !! i
                 forkIO $ forever $ philosopher name buffer left right

          forever $ do str <- atomically $ firstLogEntry buffer
                       putStrLn str

当我编译并运行我的解决方案时,似乎不存在明显的并发问题:每个哲学家最终都会吃饭,似乎没有哲学家受到青睐。但是,如果我从philosopher 中删除randomDelay 语句,编译并运行,我的程序的输出如下所示:

1 is thinking...
1 is eating...
1 is thinking...
1 is eating...
2 is thinking...
2 is eating...
2 is thinking...
2 is eating...
2 is thinking...
2 is eating...
2 is thinking...

About 2500 lines later...

2 is thinking...
2 is eating...
2 is thinking...
3 is thinking...
3 is eating...
3 is thinking...
3 is eating...

And so on...

在这种情况下发生了什么?

【问题讨论】:

  • 如果这是作业,请添加作业标签。
  • 这不是家庭作业,我在 Real World Haskell 中阅读了有关 STM 的内容,我正在努力熟悉它。
  • 使用我的设置(Debian 6 测试,ghc 7.4.1,runhaskell/ghc -O2 --make philosphys.hs)我不认为我有问题 - 哲学家 1 ..8 正在吃东西并轮流思考。你的 ghc 版本是什么,你是在编译还是使用 ghci?
  • 我在 Ubuntu 12.04 上使用 ghc 7.4.1。如果我使用-O2 编译,问题仍然存在。

标签: haskell concurrency ghc stm


【解决方案1】:

您需要使用线程运行时编译它并启用rtsopts,然后使用+RTS -N(或+RTS -Nk,其中k 是线程数。这样,​​我得到类似的输出

8 is eating...
6 is eating...
4 is thinking...
6 is thinking...
4 is eating...
7 is eating...
8 is thinking...
4 is thinking...
7 is thinking...
8 is eating...
4 is eating...
4 is thinking...
4 is eating...
6 is eating...
4 is thinking...

关键是,如果您没有多个硬件线程可供您使用,那么对于另一个哲学家来说,必须进行上下文切换。这样的上下文切换在这里并不经常发生,因为分配不多,所以每个哲学家都有很多时间思考和吃很多东西,然后轮到下一个。

有了足够多的线程,所有的哲学家都可以同时尝试伸手去拿叉子。

【讨论】:

  • 使用+RTS -N9(每个哲学家8个线程,主线程1个,写入stdout),似乎有两个哲学家在特定时间内独占CPU。
  • 你有几个核心?同时运行的线程不能多于硬件能力,所以如果你有双核机器,任何时候都不能超过两个哲学家可以竞争分叉。
  • 最好将-Nk 视为控制要使用的内核数而不是操作系统线程数。在其他情况下,如果您使用 forkOS 或拨打 FFI 电话,这很重要。
  • @DanielFischer 我的机器是双核的。你的机器有多少个内核?
  • @Alexandros 两个具有超线程的物理内核,因此取决于我尝试并行执行的操作,我最多可以同时执行四个线程(但如果线程执行相同的操作,基本上只有两个,只有 CPU 在同一个核心上的线程之间切换比 GHC 的调度器更频繁)。
猜你喜欢
  • 2017-04-10
  • 2016-03-04
  • 2019-02-07
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多