2011-12-13 16 views
1

私はC#フレームワーク2.0を使用しています。基本.NETスレッド - 単一のリーダーと単一のライタースレッドのオブジェクトを同期する最も効率的な方法

私はオブジェクトのarraylistを持っています。私はそれをシングルスレッドの読み取りと別のシングルスレッドのライターのシナリオでスレッドセーフにする必要があります。 書き込みは、配列から要素を削除または追加することを含みますが、これはおそらく、おそらくUIから週に1回発生しますが、毎秒数百回の読み取りが行われます。

私はいつもlock()を使うことができますが、この配列をスレッドセーフにするにはどうすればよいですか?

+0

あなたの代わりにリスト 'の' ArrayList'を使用しているのはなぜ 'や'一覧は '? –

答えて

3

リーダとライタのみがあるので、この場合はReaderWriterLockは役に立ちません。おそらくこの古典的なlockが最良の選択肢です。ダビド・シルバ、SpinLockで指摘したように、確かに:http://msdn.microsoft.com/en-us/library/system.threading.spinlock.aspx

編集:読者が非常に頻繁に読み取る必要があるため、多くの場合、ロックをチェックしているため、堅牢なソリューションは、スピンロックとなり、これを言った

.NET 2.0では使用できません。しかし、それはInterlocked.CompareExchangeへの繰り返しの呼び出しで実装できますが、おそらく詳細に行くのはこの段階では関係ありません。古典的なlockが最善の選択肢です。

+0

はスピンロックのように見えるFrameworkでのみ利用可能ですSpinLockクラスのフレームワーク2.0を作成するためにリフレクターを使用できるかどうかは不明ですか? –

+0

Interlockedを繰り返し呼び出して実装することができます。 CompareExchange。 – Tudor

+0

良いSpinLockを作成するにはいくつかの問題があります。私はJoe Duffyが良いことを書いていることを思い出します。それでも、私は読書のロックが全く必要ではないと思います。 –

2

lockを使用します。それが絶え間ない場合、それは非常に高速です - 確かにミリ秒の期間が可能です。

1

私はこのプロセスを最近行った。ロックされていないシングルプロデューサ/コンシューマキューのパフォーマンスの向上はほとんど無意味です(キュー/デキューあたり約0.0002ms)。

私の助言はlock()ステートメントを使用することです。あなたはまた、私が書いたロックレスバージョンを見るかもしれません。Writing a Lockless Queue for a single producer/consumerを見てください。シリーズの最後の記事にはlocking queueもあります。

+0

これはプロデューサー/コンシューマー・キューではなく、共有コレクションです。 –

+0

@Jon Hanna、The OPs statement "単一のスレッドの読み込みと別のシングルスレッドのライターのシナリオでスレッドを安全にする必要があります..."と、上記の記事にリンクされているコードは、ニーズ。彼は完璧な試合ではなく、彼がその道を行くことを主張するならば出発点になると約束した。もう一度私のアドバイスはlock()ステートメントを使うことです。 –

+0

arraylistのような構造の表面は待ち行列よりもはるかに複雑なので、私は同意しません。ロックレスバージョンを作成することはできますが、キューと同じくらい単純な方法はありません(または、スタックやキューはおそらくロックレスにするには2つの最も簡単なコンテナです)。そして、そこにさえ、 "シンプル"で "簡単"な親戚 - その2つのケースではまだ考えていることがたくさんあります。 –

4

編集:誰でもこれを見て、私はあなたが議論を見てみることをお勧めします。配列リストのオブジェクトを変更する読者スレッドに関する重要な点は、共有コレクションに関するこのクラス全体の質問にとって非常に重要です(そして、この場合、私はたぶん「全体を大きくロックする」と思うでしょう)むしろ、私はそれらの変更の詳細な分析をすることなく、ここに与えるそれでも、次はまだいくつかのケースで作業することができたアプローチより:。。


のパターンは、私は次のことを行うだろう、あなたが記述することを書き込みして読み込むために

ArrayListがalと呼ばれているとしましょう。すべての読み込みでは、すべてのスレッドの安全性を無視して読んでいます(私の狂気にはメソッドがあります)。やるウルド:ArrayListには、複数の読者のために安全であるため、

ArrayList temp = new ArrayList(al);//create copy of al 
/* code to update temp goes here */ 
al = temp;//atomic overwrite of al. 
Thread.MemoryBarrier();//make sure next read of al isn't moved by compiler or from stale cache value. 

alのコピーは、安全です。アトミックな参照を上書きするため、割り当ては安全です。実際にはほとんどの現行システムでそれをスキップすることはできますが(理論的には少なくとも理論的には必要であり、それを二度と推測しないでください)、おそらくメモリ障壁があることを確認する必要があります。

arraylistのほとんどの共有使用では、書き込みが非常に高価になるため、これはかなり恐ろしいことです。しかし、それは読書よりも60,000,000倍少ないと言われているので、バランスが合理的です。このアプローチの安全な読書の

注:

ため、私は同様のケースを考えるの誤った仮定をしていました。 次は安全である:

for(object obj in al) 
    DoSomething(obj); 

DoSomethingは長い時間がかかることがあります場合でも。これは、GetEnumerator()を一度呼び出してから、その間にalが変更されても、同じリストに関連付けられた列挙子で動作します(これは古いですが、安全です)。

次は安全ではありません。

for(int i = 0; i < al.Count; ++i) 
    DoSomething(al[i]);// 

Countは安全ですが、それはあなたがalはわずか20アイテムを持っているときにal[43]を呼び出すことを意味しますal[i]に着く時間によって古い可能性があります。それはその時点で変更されている可能性があるため、

それはあなたがalで行う第二のものにより決定するBinarySearchContainsまたはIndexOfの結果を使用しても安全BinarySearchCloneContainsIndexOfGetRangeではなくを呼び出すためにも安全です。

次も安心です。私たちはただの参照をコピーし、alをコピーしていないので、これは本当に安いです

ArrayList list = al; 
for(int i = 0; i != list.Count; ++i) 
    DoSomething(list[i]); 
DoSomethingElse(list); 
DoSomethingReallyComplicated(list); 
for(int i = 0; i != list.Count; ++i) 
SureWhyNotLetsLoopAgain(list[i]); 

注意。 listで行うことはすべて安全です。古くなった場合でも、古いリストの1つのスレッドの独自のバージョンであり、間違ってはいけないので、安全です。

So. foreach(すべて私が間違って想定していた場合)のいずれかを実行し、上記の安全な方法で呼び出した結果を返します(alで別の操作を行うかどうかを決めるのに使用しません)。alローカル変数に変換し、それに基づいて動作します。


本当に重要な注意点は、ライター・スレッドは、実際に任意のプロパティを設定したり、ArrayListの中に含まれるオブジェクトのいずれかの上の任意の非スレッドセーフのメソッドを呼び出すかどうかです。そうであれば、スレッド同期の問題が発生していないことから、数十を持つことになりました!

この場合、arraylistだけでなく、変更可能なオブジェクト(.Append("abc")StringBuilderを呼び出す方法は完全に新しいものに置き換えられない)を心配する必要がありますオブジェクトstr = "abc"の変更str)。 - 幸せな日あなたの配列リストで不変オブジェクトを持って

  1. は4つの安全の可能性があります。

  2. オブジェクトを変更することはできますが、変更することはできません。また、である必要があります。
  3. あなたはスレッドセーフなオブジェクトを持っています - 大丈夫ですが、少なくとも1つのスレッディング問題のセットは、この問題が扱うものより複雑です。
  4. 書き込みと読み取りの両方のために各オブジェクトをロックします - 競合するロックはこのような細かいロックではあまりありませんが、これは独自の重さを持ち、実際にはデフォルトよりも優れていません「ちょうどロック)とにかく争われていないとき、ロックが安いです。
+0

NB:これはあなたが上に述べたことのすべての詳細が正しいことを前提としています。これは、事例の詳細に対する最適化として、従来の知恵に対して多くのことを行います。 –

+0

+1すてきな答え、きれいでシンプル:) –

+0

あなたが書いたコードは、arraylistを読んでいる読者スレッドルーチンに入りますか?これが事実ならば、「新」をすることは、それがたくさん呼ばれるほど私にとっては高価です。 – bsobaid

関連する問題