それは実際にスレッドセーフですが、(純粋にCount
上の実装の詳細の問題として):コードの
スレッドセーフなスニペットがないスレッドセーフなアプリケーション作るのですか。異なるスレッドセーフな操作をスレッドセーフでない操作に組み合わせることができます。実際、多くのスレッドセーフでないコードは、スレッドセーフである小さな部分に分割することができます。
あなたが望んでいた理由からスレッドセーフではありません。つまり、それをさらに拡張するとスレッドセーフではありません。
このコードはスレッドセーフになります:任意のリストと
public void CallToMethodInOtherClass(List<int> list)
{
//note we've no locks!
int i = list.Count;
//do something with i but don't touch list again.
}
コールそれを、そしてそれは関係なく、他のスレッドが何であるかを、そのリストの状態に基づいて値をi
あげますまでlist
は壊れません。 i
に無効な値が設定されることはありません。だから、
このコードは、スレッドセーフである間:
public void CallToMethodInOtherClass(List<int> list)
{
Console.WriteLine(list[93]); // obviously only works if there's at least 94 items
// but that's nothing to do with thread-safety
}
このコードはスレッドセーフではありません。
public void CallToMethodInOtherClass(List<int> list)
{
lock(locker)//same as in the question, different locker to that used elsewhere.
{
int i = list.Count;
if(i > 93)
Console.WriteLine(list[93]);
}
}
先に進む前に、私はスレッドように説明2ビット安全はリストの仕様であると約束されていません。保守的なコーディングでは、実装の詳細に依存するのではなく、スレッドセーフではないとみなされますが、重要な方法でロックを使用する方法の問題に影響するため、実装の詳細に依存します:
最初にlocker
のロックを取得していないlist
で動作するコードは、そのコードがCallToMethodInOtherClass
と同時に実行されることを妨げられません。さて、list.Count
はスレッドセーフでlist[93]
はトレッドセーフですが、2番目の動作を確実にするために最初に依存する2つの組み合わせは、スレッドセーフではありません。ロック外のコードはlist
に影響する可能性があるため、またはClear
をCount
の間で呼び出し、list[93]
が動作し、list[93]
が呼び出されることがあります。
list
がこれまでに追加されたことがわかっている場合は、サイズ変更が同時に発生しても、いずれかの方法でlist[93]
という値になります。何かがlist[93]
に書き込んでいて、.NETがアトミックに書き込むタイプ(そしてint
がそのようなタイプの1つです)の場合、正しくロックしたかのように、古いものか新しいもののどちらかになりますどのスレッドが最初にロックに行くかによって古いものか新しいものかを取得します。また、これは指定された約束の実装の詳細ではありませんが、スレッド安全性がまだスレッドセーフなコードではないことを指摘しています。
これを実際のコードに移動します。 list.Count
とlist[93]
はスレッドセーフであると仮定すべきではありません。なぜなら、私たちは、それが変更される可能性があると約束されていないためですが、その約束があったとしても、それらの約束は合致しませんスレッドセーフです。
重要なことは、相互に干渉する可能性のあるブロックのコードを保護するために同じロックを使用することです。したがって、その下のバリアントはスレッドセーフであることが保証されて考えてみましょう。
public class ThreadSafeList
{
private readonly object locker = new object();
private List<int> myList = new List<int>();
public void Add(int item)
{
lock(locker)
myList.Add(item);
}
public void Clear()
{
lock(locker)
myList.Clear();
}
public int Count
{
lock(locker)
return myList.Count;
}
public int Item(int index)
{
lock(locker)
return myList[index];
}
}
このクラスは、それが行うすべてにスレッドセーフであることが保証されます。実装の詳細に依存せずに、同じインスタンスで別のスレッドが行っているために、状態が破損したり不正確な結果が返されるメソッドはありません。次のコードはまだはしかし動作しません:
// (l is a ThreadSafeList visible to multiple threads.
if(l.Count > 0)
Console.WriteLine(l[0]);
を私たちは、各呼び出し100%のスレッドの安全性を保証してきましたが、我々は組み合わせを保証していない、と私たちが保証することはできません組み合わせ。
私たちができることは2つあります。その組み合わせのためのメソッドを追加することができます。次のようなものは、特にマルチスレッドの使用のために設計された多くのクラスのための共通のようになります。
public bool TryGetItem(int index, out int value)
{
lock(locker)
{
if(l.Count > index)
{
value = l[index];
return true;
}
value = 0;
return false;
}
}
これは、カウントテストとスレッドセーフであることが保証された単一の操作の項目の検索部分になります。
また
、最も頻繁に私達は何をする必要があるか、我々はロック操作がグループ化されている場所で起こるがあります。もちろん
lock(lockerOnL)//used by every other piece of code operating on l
if(l.Count > 0)
Console.WriteLine(l[0]);
、これは冗長ThreadSafeList
とちょうど廃棄物の中にロックを作ります努力、宇宙、そして時間のこれは、ほとんどのクラスがインスタンスメンバーにスレッドセーフティを提供しない主な理由です。クラス内のメンバーのコールグループを意味のある方法で保護できないため、スレッドセーフが約束していない限り、彼ら自身で非常によく指定され、有用である。戻ってあなたの問題のコードに来て
:OtherClass
が内部でロックするための独自の理由がない限り、CallToMethodInOtherClass
に
ロックが除去されなければなりません。それは非スレッドセーフな方法で結合されず、プログラムにロックを追加するだけで、デッドロックがないことを確かめるためにそれを分析する複雑さが増すという意味のある約束をすることはできません。
CallToMethodInOtherClass
への呼び出しは、そのクラス内の他の操作と同じロックによって保護されるべきである。
public void MethodeB()
{
lock(locker)
CallToMethodInOtherClass(myList);
}
その後限りCallToMethodInOtherClass
が、それは後に他のスレッドが見ることができるどこかmyList
が格納されていないとして、 myList
にアクセスできる唯一のコードは、myList
の他の操作と並行して呼び出さないことを保証するため、CallToMethodInOtherClass
はスレッドセーフではありません。
二つの重要な事柄があります:
何かを「スレッドセーフ」と記載されている場合は、スレッドセーフ」に該当約束の異なる種類があるので、それは、それによって有望だだけで何を知っています「それを意味するものではありませんが、私はこの物体を無意味な状態に置かないでしょう」ということを意味しています。おそらくこれでボールをプレーしていない他のスレッドがないことができるように、同じデータに影響を与え、そしてオブジェクトへのアクセスを守るよ、グループごとに同じロック付きグループ事業のに
ロック、。
*これはスレッドセーフの定義が非常に限定されています。 List<T>
のlist[93]
を呼び出します。T
は、原子的に書き込まれ、読み取られるタイプで、実際に少なくとも94個のアイテムがあるかどうかわかりません。他のスレッドが動作しているかどうかにかかわらず、等しく安全です。もちろん、どちらの場合でもArgumentOutOfRangeException
を投げることができるということは、ほとんどの人が「安全」と考えるようなものではありませんが、複数のスレッドでの保証は同じです。単一のスレッドでCount
をチェックすることでより強力な保証を得ることができますが、マルチスレッドの状況ではスレッドセーフではないと記述することができません。そのコンボはまだ状態を破壊しませんが、私たち自身が起こることができないことを保証した例外につながる可能性があります。
バスルームには2つのドアがあり、それぞれにロックが付いています。あなたの質問は、「私がシャワーを浴びているときに初めてロックをロックすると仮定し、シャワーにいるときに友人のボブだけがロックをロックしています。明らかにはい!あなたとボブが一緒にシャワーを避けたいのであれば、同じロック*を使うことに同意する必要があります。このように、スレッドセーフなオブジェクトにはアクセスできません。 –