3

プロデューサーとコンシューマー (「クライアント」) がブロードキャスト メッセージを相互に送信したい、つまりn:m関係があるアプリケーションがあります。すべてが異なるプログラムである可能性があるため、それらは異なるプロセスであり、スレッドではありません。

より保守しやすいものに減らすためにn:m、小さな中央サーバーを導入するようなセットアップを考えていました。そのサーバーは、各クライアントが接続するソケットを提供します。
そして、各クライアントはそのソケットを介してサーバーに新しいメッセージを送信し、1:n.

サーバーは、クライアントに対して読み取り専用の共有メモリも提供します。これは、サーバーによって新しいメッセージが追加され、古いメッセージが上書きされるリング バッファーとして編成されます。 これにより、クライアントがメッセージを処理する時間が与えられますが、遅すぎる場合は不運であり、いずれにせよ関係がなくなります...

このアプローチの利点は、同期だけでなく、不要なデータのコピーやバッファ階層を回避できることです。中心的なもので十分なのではないでしょうか?

それがこれまでのアーキテクチャです - 理にかなっているといいのですが...

次に、それを実装することのより興味深い側面について説明します。
リング バッファー内の最新の要素のインデックスは共有メモリ内の変数であり、クライアントはそれが変更されるまで待つ必要があります。愚かなのではなくwhile( central_index == my_last_processed_index ) { /* do nothing */ }、たとえばを使用してCPUリソースを解放したいpthread_cond_wait().

しかし、それには必要ないと思うミューテックスが必要です-一方で、なぜpthreadの条件変数関数にはミューテックスが必要なのですか? 私のアーキテクチャが理にかなっており、そのように実装できるかどうかを尋ねたほうがよいという印象を受けました...

それがすべて理にかなっており、うまくいくかどうか、ヒントを教えてもらえますか?

(補足: クライアント プログラムは、Perl や Python などの一般的なスクリプト言語で作成することもできます。そのため、サーバーとの通信はそこで再作成する必要があるため、複雑すぎたり、独自のものであってはなりません)

4

3 に答える 3

3

メモリが機能する場合、条件変数にミューテックスが付随する理由は、POSIX では、条件変数を通知すると、カーネルが条件変数のすべてのウェイターを起動するためです。このような状況では、消費者スレッドが最初に行う必要があるのは、生産者スレッドと消費者スレッドの間で共有される変数にアクセスすることによって、消費するものがあることを確認することです。ミューテックスは、この目的で使用される変数への同時アクセスから保護します。これはもちろん、多数のコンシューマが存在する場合、そのうちの n-1 個が不必要に起こされることを意味します。

上記の配置を正確に実装したので、使用する IPC オブジェクトの選択は自明ではありません。個別のプロセスで優先度の高いリアルタイム スレッド間でオーディオをバッファリングしていたため、コンシューマーをブロックしたくありませんでした。オーディオはリアルタイムで生成および消費されていたため、すでに両端で定期的にスケジュールが設定されていました。消費する (または生成するスペースがない) 場合は、締め切りに間に合わなかったため、データを廃棄しました。

あなたが説明する配置では、消費者がキューに入れられたアイテムを同時に消費するのを防ぐためにミューテックスが必要になります(そして、負荷の軽いSMPシステムではそうなるでしょう)。ただし、プロデューサーにこれについても争ってもらう必要はありません。

消費者が共有メモリへの読み取り専用アクセス権を持っていることについてあなたがコメントしていることを理解できません。従来のロックレス リング バッファの実装では、プロデューサーがキューのテール ポインターを書き込み、コンシューマーがヘッドを書き込みますが、すべての関係者は両方を読み取ることができる必要があります。もちろん、キューの先頭と末尾を、キュー データ自体とは別の共有メモリ領域に配置することもできます。

また、このようなリング バッファを実装する場合、SMP システムには理論上のデータ コヒーレンシの危険性があることに注意してください。つまり、ヘッドまたはテール ポインタに関するキューの内容のメモリへの書き戻しが順不同で発生する可能性があります (それらはキャッシュ - 通常は CPU コアごと)。このテーマには、CPU 間のキャッシュの同期を行うための他のバリエーションがあります。これらを防ぐには、メモリ、ロード、ストアのバリアで順序付けを行う必要があります。ウィキペディアのメモリ バリアを参照してください。ミューテックスや条件変数などのカーネル同期プリミティブを使用して、この危険を明示的に回避します。

C11 アトミック操作はこれに役立ちます。

于 2012-08-03T23:05:01.973 に答える
2

sem_tシステムにそれらがある場合は、を使用して、おそらく少し異なるデザインにすることができます。一部の POSIX システムは、まだ 2001 バージョンの POSIX のままです。

おそらく、ミューテックス/条件のペアを強制的に必要とすることはありません。これは、POSIX 用にずっと前に設計された方法です。

最新の C、C11、および C++、C++11 は、現在のすべてのプロセッサに実装されている機能であるアトミック操作を提供します (または提供する予定ですが、ほとんどの高等言語からのサポートが不足していました)。アトミック操作は、実装したいリング バッファーの競合状態を解決するための答えの一部です。しかし、それらでは十分ではありません。それらを使用すると、ポーリングを介してアクティブな待機しかできないためです。これはおそらくあなたが望むものではありません。

Linux は POSIX の拡張として、両方の問題を解決するfutexを備えています。つまり、アトミック操作を使用して更新の競合を回避し、システム コールを介してウェイターをスリープ状態にする機能です。Futex は、日常のプログラミングにはレベルが低すぎると思われがちですが、実際に使用するのはそれほど難しいことではないと思います。私はここに物事を書きました。

于 2012-08-03T22:07:07.380 に答える