簡體   English   中英

如何使管道與Haskell的管道庫並發?

[英]How can I make a Pipe concurrent with Haskell's Pipe library?

我有一些使用Pipes的Haskell代碼:

module Main(main) where
import Pipes

a :: Producer Int IO ()
a = each [1..10]

b :: Pipe Int Int IO ()
b = do
  x <- await
  yield (x*2)
  b

c :: Consumer Int IO ()
c = do
  x <- await
  lift $ print x
  c

main :: IO ()
main = runEffect $ a >-> b >-> c

Pipes.Concurrent教程演示了如何使用多個工作人員以及工作竊取。 我怎樣才能在b內做類似的事情? 我希望b使用一定數量的工人同時執行它的工作。

顯然,並發性在這個確切的情況下沒有用,但它是我能想到的最簡單的例子。 在我的實際用例中,我想使用有限數量的工作者同時發出一些Web請求。

編輯:我誤解了你的要求; 你可以在管道內做到這一點,但我不確定它的動機是什么。 我建議建立可重復使用的管道鏈,只使用工人調度它們,而不是試圖在管道內部建立工人。 如果您將其構建到管道本身,那么您將失去任何排序保證,即第一個輸入是第一個輸出。

關於工作竊取的部分是你正在尋找的,這段代碼基本上是從教程中逐字逐句,但讓我們分解它是如何工作的。 這是我們可以做你想做的一種方式:

module Main(main) where
import Pipes
import Pipes.Concurrent

import Control.Concurrent.Async (async, wait)
import Control.Concurrent (threadDelay)
import Control.Monad (forM)

a :: Producer Int IO ()
a = each [1..10]

b :: Pipe Int Int IO ()
b = do
  x <- await
  yield (x*2)
  b

c :: Consumer Int IO ()
c = do
  x <- await
  lift $ print x
  c

main :: IO ()
main = do
  (output, input) <- spawn unbounded
  feeder <- async $ do runEffect $ a >-> toOutput output
                       performGC

  workers <- forM [1..3] $ \i ->
    async $ do runEffect $ fromInput input  >-> b >-> c
               performGC

  mapM_ wait (feeder:workers)

第一行spawn unbounded來自Pipes.Concurrent,它初始化一個具有輸入和輸出句柄的“郵箱”。 起初它讓我困惑,但在這種情況下,我們將消息發送到輸出並從輸入中拉出它們。 這類似於golang等語言中的推拉消息通道。

我們指定一個Buffer來說明我們可以存儲多少條消息,在這種情況下我們設置無限制的無限制。

好的,所以郵箱已初始化,我們現在可以創建向其發送消息的Effect 郵箱通道是使用STM實現的,因此它可以異步收集消息。

讓我們創建一個異步作業來為郵箱提供信息;

feeder <- async $ do runEffect $ a >-> toOutput output
                     performGC

a >-> toOutput output僅僅是正常的管道組成,我們需要toOutput輸出轉換成管道。 注意performGC調用也是IO的一部分,它允許Pipes.Concurrent知道在作業完成后進行清理。 如果我們願意,我們可以使用forkIO運行它,但在這種情況下我們使用async以便我們可以等待結果稍后完成。 好的,所以我們的郵箱應該異步接收消息,讓我們把它們拉出去做一些工作。

workers <- forM [1..3] $ \i ->
  async $ do runEffect $ fromInput input  >-> b >-> c
             performGC

和以前一樣,但這次我們只是產生了一些。 我們從輸入讀取就像使用fromInput的普通管道fromInput ,然后在我們鏈的其余部分運行它,在我們完成時清理。 input將確保每次拉出一個只有一個工人接收它的值。 當所有輸入到output的作業完成后(它跟蹤所有打開的作業),它將關閉input管道,工人將完成。

如果你在web-worker場景中使用它,你將有一個主循環,它不斷向toOutput output通道發送請求,然后產生toOutput output工作人員,他們從fromInput input進入他們的管道。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM