为何无MVar线程被抢占时GHC公平性保证的交替模式失效?
GHC线程公平性为何未在无MVar的putChar示例中体现?
问题背景
运行以下Haskell代码:
import Control.Concurrent import Control.Monad import System.IO main :: IO () main = do hSetBuffering stdout NoBuffering _ <- forkIO (replicateM_ 100000 (putChar 'A')) replicateM_ 100000 (putChar 'B')
输出会出现类似BBBBBBBABABABAAAAAAA的结果,预期的完美交替模式被打破。但Simon Marlow所著《Parallel and Concurrent Programming in Haskell》第140页指出,只有线程未持有MVar时被抢占才会打破公平性保证带来的完美交替,而该示例并未使用MVar,为何GHC的公平性保证未体现?
解答
首先要明确GHC线程调度器的公平性定义:它保证的是所有可运行线程最终都会被调度执行,不会出现线程饿死的情况,但这并不等同于会在单个操作(比如putChar)执行后立刻切换线程,实现细粒度的完美交替。
具体到这个示例:
putChar是轻量操作,GHC调度器不会在每次调用putChar后强制触发线程切换。调度器的抢占触发点通常是在内存分配、或者线程执行了一定数量的CPU指令(由RTS的tick计数器阈值控制)之后。- 当主线程或fork出的线程连续执行多个
putChar操作才触发一次抢占检查时,就会出现连续的B或A输出,打破所谓的“完美交替”。 - 书中提到的“完美交替”场景,是依赖MVar同步的情况:线程在执行完操作后会主动释放MVar并进入阻塞状态,此时调度器会切换到等待MVar的另一个线程执行。这种阻塞/唤醒的同步机制会强制线程在每次操作后切换,从而形成严格交替。而本示例中两个线程都是持续可运行的,没有主动阻塞的动作,调度器只会保证长期的公平性(最终两个线程都能完成所有100000次
putChar),不会为了细粒度交替而频繁切换。
简单来说,GHC的公平性是全局长期的调度公平,而非操作级别的即时交替保证。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

