2009-06-15 12 views
0

私は、データをスレッドセーフにする「最良の」方法が何であるか疑問に思っています。スレッドの安全性...私の "ベスト"な行動は何ですか?

具体的には、複数のスレッド間でリンクリストを保護する必要があります。あるスレッドは、別のスレッドがデータを追加/削除したり、リスト全体を解放しているうちに読み込もうとする可能性があります。私はロックについて読んできました。彼らは最も一般的に使用されているアプローチのようですが、明らかに問題になることがあります(デッドロック)。私は、アトミック操作とスレッドローカルストレージについても読んできました。

あなたの意見では、私の最善の行動は何でしょうか?ほとんどのプログラマーがどのようなアプローチを採用していますか?

+2

最良の方法は使用している言語によって異なります。例えば、erlangの場合は、 –

+0

で、erlangの_intrinsically_ message-passing(マルチプロセッシングはその中で最も甘いものです)です。私の答えは、Pythonなどの他の言語でその幸せな状態を構築する方法を描いたものです。 –

答えて

5

頻繁に使用されるが、非常に健全ではない一つのアプローチは、すべての「共有」構造を所有する一つの特別な目的のスレッドを指定することです。そのスレッドは一般的に(スレッドセーフな;-)キューに待機しています。 PythonのQueue.Queueインスタンスでは、作業要求(共有構造の読み取りまたは変更)のために、応答を要求するもの(準備完了時に応答が配置される独自のキューを渡します)とそうでないものを含みます。このアプローチは、共有リソースへのすべてのアクセスを完全にシリアライズし、マルチプロセスまたは分散アーキテクチャーに簡単に再マップします(Pythonではほとんど無制限に、multiprocessing ;-)となり、健全性とデッドロックの欠如だけでなく、基礎となる待ち行列オブジェクトは、一度、そしてすべてのために十分にプログラムされている。

これは基本的に、共有データ構造の欠点をメッセージ通過並列アーキテクチャの楽園に変えます。

OTOH、は、ロック&c ;-)でハードな方法でスラッグするよりも、オーバーヘッドが高いことがあります。

+0

が同意します。私はむしろ、同時接続の追加/削除とリンクされたリスト上のトラバーサルをサポートするよりも、メッセージをベースとするインターフェースに移行する方がはるかに優れています。 – Rick

+0

純粋なメッセージパッシングシステムでは、依然としてデッドロックについて注意する必要があります。プロセスAがプロセスBからメッセージbを待ってから、プロセスAにメッセージaを送る前に、プロセスBはメッセージbを送信しますプロセスCからメッセージcを受け取ります。プロセスCは、メッセージcを受信したときにメッセージcを出すだけです。ロックされたリソースではなく、メッセージの依存関係にのみ依存するデッドロックと同じ基本的な問題。 –

+0

Windows APIでは、これはSendMessage(同期であり、送信が受信されて処理されるまで送信者をブロックする)とPostMessage(非同期)の違いです。非同期のメッセージ転送ではデッドロックが発生しません(ただし、実際に投稿が実際に受信されて処理されたかどうかは疑問に思うかもしれません)。 – ChrisW

0

スレッドの安全性の最も重要なルールを常に忘れないでください。あなたのコードのクリティカルセクションを全て知っておいてください。それで、あなたのABCのようにそれらを知ってください。あなたがそれらを一度確認することができる場合にのみ、あなたのスレッドセーフティメカニズムを動作させる領域を知っていますか?その後

、親指のルールを覚えている:

  • は、ヒープ上のすべてのごグローバル 変数/変数を探してください。
  • サブルーチンが リエントラントであることを確認してください。
  • 共有データへのアクセスが にシリアライズされていることを確認してください。
  • ポインターを介して間接的に アクセスがないことを確認してください。

(私は他の人がより多くを追加することができます確信しています。)

0

安全性の観点からは、データ構造全体にロックを設定して、一度に1つのスレッドしかそれに触れないようにすることが最善の方法です。

構造全体よりもロックすることを決めると、おそらくパフォーマンス上の理由から、これを行う詳細はいたずらになり、すべてのデータ構造や同じ構造のバリアントでも異なります。

私の提案は、あなたのデータ構造上のグローバルロックと

  1. 開始することです。あなたのプログラムをプロファイルして、それが本当に問題かどうかを確認してください。

  2. 問題が発生した場合は、他の方法で問題を配布するかどうかを検討してください。問題のデータ構造内のデータの量を最小限に抑えることができるので、頻繁にアクセスする必要はありませんか?たとえば、キュ​​ーイングシステムの場合は、スレッドごとにローカルキューを保持し、ローカルキューがオーバーロードまたはアンロードされた場合にのみグローバルキューに出入りするようにすることができます。

  3. あなたがやっている特定の種類の競合を減らすために設計されたデータ構造を見て、安全の面で間違って慎重に正確に実装してください。キューイングの例では、必要なものが作業盗み出しキューである可能性があります。

+0

このアプローチでは、「構造体全体」が自己完結しているため、一度に複数のオブジェクトをロックする必要はありません。通常、構造体ロックに対するグローバルな問題は、ある構造体のデータが別の構造体のデータを指している場合に発生します。デッドロックが一般的な場所になります。 – jmucchiello

+0

実際に、複数の構造体をロックする必要があり、複数のロックを使用すると、一度にすべてをロックするロックではなく、複数のロックを使用してデータ構造の異なる部分をロックするのと同じポジションになります。 –

2

不変のコレクションと考えることができます。 .netの文字列がReplace、Insertなどのメソッドを持つのと同じように、文字列は変更されず、新しいものが作成されます.LinkedListコレクションは不変であるようにも設計できます。実際、LinkedListは実際には他のコレクションデータ構造と比べてこのように実装するのがかなり簡単です。

ここには、不変のコレクションと.NETでのいくつかの実装へのリンクを議論するブログ記事へのリンクがあります。

http://blogs.msdn.com/jaredpar/archive/2009/04/06/immutable-vs-mutable-collection-performance.aspx

+0

これらの種類のデータ構造は、しばしば「永続的な」データ構造として知られています(そして、これについての議論のためにSO上に「永続的なデータ構造」タグがあります)。これらは、以前のバージョンのデータ構造とメモリセルを共有することが多く、コピー(データ構造の以前のバージョンを保持している場合はメモリ使用量)を削減します。 –

関連する問題