管道:多流消费者

时间:2013-07-29 18:03:10

标签: haskell nlp conduit

我写了一个计算语料库中NGrams频率的程序。我已经拥有一个消耗代币流并生成单个订单NGrams的函数:

ngram :: Monad m => Int -> Conduit t m [t]
trigrams = ngram 3
countFreq :: (Ord t, Monad m) => Consumer [t] m (Map [t] Int)

目前我只能将一个流消费者连接到流源:

tokens --- trigrams --- countFreq

如何将多个流使用者连接到同一个流源? 我想有这样的事情:

           .--- unigrams --- countFreq
           |--- bigrams  --- countFreq
tokens ----|--- trigrams --- countFreq
           '--- ...      --- countFreq

加号是并行运行每个消费者

修改 感谢Petr,我提出了这个解决方案

spawnMultiple orders = do
    chan <- atomically newBroadcastTMChan

    results <- forM orders $ \_ -> newEmptyMVar
    threads <- forM (zip results orders) $
                        forkIO . uncurry (sink chan)

    forkIO . runResourceT $ sourceFile "test.txt"
                         $$ javascriptTokenizer
                         =$ sinkTMChan chan

    forM results readMVar

    where
        sink chan result n = do
            chan' <- atomically $ dupTMChan chan
            freqs <- runResourceT $ sourceTMChan chan'
                                 $$ ngram n
                                 =$ frequencies
            putMVar result freqs

1 个答案:

答案 0 :(得分:5)

我假设您希望所有接收器都能接收所有值。

我建议:

  1. 使用newBroadcastTMChan创建新频道Control.Concurrent.STM.TMChan(stm-chans)。
  2. 使用此频道使用来自Data.Conduit.TMChan(stm-conduit)的sinkTBMChan为您的主要制作人构建接收器。
  3. 为每个客户使用dupTMChan创建自己的副本以供阅读。使用sourceTBMChan
  4. 启动将读取此副本的新主题
  5. 从您的主题中收集结果。
  6. 确保您的客户端能够在生成数据时尽快读取数据,否则可能会导致堆溢出。
  7. (我没试过,请告诉我们它的工作原理。)


    更新:收集结果的一种方法是为每个消费者线程创建一个MVar。它们中的每一个都会在结束后putMVar结果。并且你的主线程将takeMVar放在所有这些MVar上,从而等待每个线程完成。例如,如果vars是您MVar的列表,则主线程会发出mapM takeMVar vars来收集所有结果。