5

私は、パイプ インターフェイスでのエンコーディング用に C ライブラリをラップしている最中ですが、いくつかの設計上の決定を行う必要があることに気付きました。

C ライブラリがセットアップされた後、エンコーダ コンテキストを保持します。これにより、いくつかのパラメーターをエンコードまたは変更できます (この最後の関数への Haskell インターフェースを呼び出しましょうtune :: Context -> Int -> IO ())。私の質問には2つの部分があります:

  1. エンコーディング部分は で簡単にラップできますが、Pipe Foo Bar IO ()も公開したいと思いtuneます。エンコーディング コンテキストの同時使用はロックで保護する必要があるため、パイプ内の反復ごとにロックをtune取得し、同じロックを取得して保護する必要があります。しかし今、ユーザーに隠しロックを強制しているように感じます。ここで間違ったツリーを吠えていますか? この種の状況は通常、パイプのエコシステムでどのように解決されますか? 私の場合、特定のコードが含まれるパイプは常に独自のスレッドで実行され、同時にチューニングが行われることを期待していますが、この観点をユーザーに強制したくありません。パイプエコシステムの他のパッケージは、ユーザーにどちらかを強制することはないようです。
  2. 使用されなくなったエンコーディング コンテキストは、適切に初期化解除する必要があります。パイプのエコシステムではIO、パイプが破壊されたときにそのようなこと (この場合はいくつかのアクションを実行すること) が確実に処理されるようにするにはどうすればよいでしょうか?

具体的な例は、圧縮ライブラリをラップすることです。この場合、上記は次のようになります。

  1. 圧縮強度は調整可能です。パイプを設置すると、楽しそうに流れていきます。圧縮コーデック コンテキストへの同時アクセスをシリアル化する必要があると仮定すると、パイプの実行中に圧縮強度の設定を変更できるようにするにはどうすればよいでしょうか?
  2. 圧縮ライブラリは、セットアップ時に Haskell ヒープから大量のメモリを割り当てました。パイプが破棄されたときに、ライブラリ関数を呼び出してこれをクリーンアップする必要があります。

ありがとう…これはすべて明白かもしれませんが、私はパイプのエコシステムにまったく慣れていません。

編集:投稿後にこれを読んで、これが私が今までここで尋ねた中で最も漠然とした質問だと確信しています. うーん!ごめん ;-)

4

1 に答える 1

4

(1) に関しては、一般的な解決策は、Pipeのタイプを次のように変更することです。

Pipe (Either (Context, Int) Foo) Bar IO ()

つまり、内部で処理するFoo入力と要求の両方を受け入れます。tune

Producer次に、入力と調整リクエストに対応する2 つの同時実行があると仮定します。

producer1 :: Producer Foo IO ()

producer2 :: Producer (Context, Int) IO ()

pipes-concurrency次のように、両方がフィードするバッファを作成するために使用できます。

example = do
    (output, input) <- spawn Unbounded
    -- input  :: Input  (Either (Context, Int) Foo)
    -- output :: Output (Either (Context, Int) Foo)

    let io1 = runEffect $ producer1 >-> Pipes.Prelude.map Right >-> toOutput output
        io2 = runEffect $ producer2 >-> Pipes.Prelude.map Left  >-> toOutput output
    as <- mapM async [io1, io2]
    runEffect (fromInput >-> yourPipe >-> someConsumer)
    mapM_ wait as

ライブラリの詳細については、このチュートリアルpipes-concurrencyを読むことで学習できます。

すべての調整要求が同じシングル スレッドを通過するように強制するPipeことで、関数の 2 つの同時呼び出しが誤って発生しないようにすることができますtune

(2) については、 を使用してリソースを取得する方法が 2 つありますpipes。より洗練されたアプローチは、内で使用できる関数pipes-safeを提供するライブラリを使用することですが、これはおそらくあなたの目的には過剰であり、パイプの存続期間中に複数のリソースを取得および解放するためだけに存在します。より簡単な解決策は、次のイディオムを使用してパイプを取得することです。bracketPipewith

withEncoder :: (Pipe Foo Bar IO () -> IO r) -> IO r
withEncoder k = bracket acquire release $ \resource -> do
    k (createPipeFromResource resource)

次に、ユーザーは次のように記述します。

withEncoder $ \yourPipe -> do
    runEffect (someProducer >-> yourPipe >-> someConsumer)

必要に応じてパッケージを使用できますmanaged。これにより、型が少し簡素化され、複数のリソースの取得が容易になります。詳細については、私のこのブログ投稿を読んでください。

于 2014-09-06T21:32:48.893 に答える