2012-01-04 23 views
12

私は、すべてのスレッドがメソッドの変数のコピーを独自のスタックに持っていることを正しく理解していますので、静的メソッドが別のスレッドから呼び出されたときに問題はありませんか?C#クラスを静的メソッドと変数だけで使用すると、並行性の問題が発生する可能性がありますか?

+0

もしそれが厳しく制限されていれば - *多分*。しかし、それは非常に興味深い方法のようには聞こえません。 – Yuck

+0

私は、静的メソッドへのアクセス権を持つパブリッククラスを持つことができる、例えば逆の場合に問題が発生すると考えています。 – MethodMan

+0

これは便利です:http://csharpindepth.com/Articles/General/Singleton.aspx – David

答えて

16

はい、いいえ。パラメータが値型であれば、それは彼ら自身のコピーを持っています。または、参照タイプが不変の場合、変更することはできず、問題はありません。しかし、パラメータが変更可能な参照型である場合、引き渡される引数で考慮するスレッド安全性の問題がまだあります。

意味がありますか?引数として参照型を渡すと、参照は "値で渡される"ので、古いオブジェクトを参照する新しい参照になります。したがって、スレッドセーフではない方法で同じオブジェクトを変更する可能性のある2つの異なるスレッドを持つことができます。

これらのインスタンスを作成してそれらを使用しているスレッドでは、少ししか取得できない可能性がありますが、静的メソッドを使用しているからといって、地元/パラメータはであり、スレッド安全性の保証ではありません。

+0

+1は、静的(またはその欠如)の使用が何も保証しないことに注意してください。静的でない方法に対しても同じ規則が適用されます。 –

+0

ありがとう!私の仕事はずっと楽になります。 –

+0

@ChrisShain:本当に、私はそれを明示的に言及すべきでした、私はそれが暗示されることを望んでいました。 –

2

すべての変数が不変の参照型または値型でない限り、そのような保証はありません。

変数が可変参照型である場合は、適切な同期を実行する必要があります。

EDIT:スレッド間で変数を共有する場合は、変数を同期する必要があります。メソッドの外部に公開されていないローカルで宣言された変数は、同期する必要はありません。

+0

ローカルで宣言されて使用される変更可能な参照は、同期を必要としません。 – Tudor

+0

真、それに応じて編集します。 –

0

はい、方法はのみローカルスコープの変数となし任意の gloval変数を使用しない限り、そうこれはtrueある場合ことのいずれかの方法で、任意のオブジェクトの状態に影響を与える可能性がどのような方法がありませんにあり、あなたはマルチスレッドで使用するのに問題はありません。私は、この条件でさえ、staticであっても、そうでなくても関係ないと言います。

0

これらがメソッドのローカル変数である場合は、「はい」を心配する必要はありません。参照によってパラメータを渡したり、グローバル変数にアクセスしたり、異なるスレッドでグローバル変数にアクセスしたりしないようにしてください。それであなたは困っているでしょう。

0

staticメソッドは、スレッドセーフでないかもしれないstaticフィールドのデータをそのクラスまたはその外部のいずれかで参照できます。

最終的には、問題はあるかもしれないので、あなたの質問に対する答えは「ノー」ですが、通常はありません。

0

2つのスレッドは、異なるオブジェクト上のメソッドに渡されるオブジェクトによって同じオブジェクト上で動作することができます。また、オブジェクトがSingletonなどでグローバルにアクセスできる場合は、すべてのベットはオフです。

マーク

9

は、私は、静的メソッドは別のスレッドから呼び出されたときに問題がないように、すべてのスレッドが独自のスタック内のメソッドの変数のコピーを持っていることを正しく理解していますか?

まず第一に、「すべてのスレッドが独自のスタック内のメソッドのローカル変数のコピーを持っている。」という偽でありますローカル変数は、寿命が短い場合にのみスタック上に生成されます。ローカル変数は、(1)外部変数のクローズオーバー、(2)イテレータブロックでの宣言、または(3)非同期メソッドでの宣言であれば、任意の長さの生存期間を持つことができます。

これらのすべてのケースでは、あるスレッドのメソッドのアクティブ化によって作成されたローカル変数を、後で複数のスレッドによって変更することができます。これはスレッドセーフではありません。

第2に、異なるスレッドから静的メソッドを呼び出すときに問題が発生する可能性があります。ローカル変数が時々スタックに割り当てられるという事実は、静的メソッドによる共有メモリへのアクセスを魔法のように突然正しいものにしません。

静的メソッドと変数のみを使用してC#クラスを使用すると、並行性の問題が発生する可能性がありますか?

「ローカル変数なし」ではなく、「静的変数なし」を意味します。

もちろん、があります。たとえば、静的変数を持たず、非静的メソッドもなく、2番目のスレッドとは別のオブジェクトも作成されていないプログラムと、そのスレッドへの参照を保持する1つのローカル変数があります。 cctor以外の方法は実際にはには何もしません。このプログラムはデッドロックしています。あなたのプログラムがスレッドのバグを含んでいないという単純な単純なものだと仮定することはできません!

読者に練習してください:ロックを含まないと思われるこのプログラムが実際にデッドロックする理由を説明してください。

class MyClass 
{ 
    static MyClass() 
    { 
     // Let's run the initialization on another thread! 
     var thread = new System.Threading.Thread(Initialize); 
     thread.Start(); 
     thread.Join(); 
    } 

    static void Initialize() 
    { /* TODO: Add initialization code */ } 

    static void Main() 
    { } 
} 

あなたのプログラムにスレッドの問題がないことを知る魔法のような方法を探しているようです。それを知るそのような魔法の方法はありませんが、それをシングルスレッドにするのは簡単ではありません。スレッドや共有データ構造の使用状況を分析する必要があります。

+2

静的メンバーは、すべての静的メンバー(この場合は 'Initialize'メソッド)の実行が許可される前に完了することが保証されますが、ctorはそのctorを待機しているスレッドを待ってビジー状態です。 –

+0

@ AlbinSunnanbo:あなたは正しい考えを持っています。しかし、もちろん、cctorがスレッドを生成せずにInitializeを呼び出した場合、静的コンストラクターが実行を終了する前に実行されている静的メンバーであっても、呼び出しは許可されます。より正確な問題の記述方法は、次のとおりです。cctorが最初のスレッドで実行されている間に2番目のスレッドが静的メンバにアクセスしようとすると、2番目のスレッドはcctorをスキップして最初のスレッドが終了するのを待ちます。静的メンバー。しかし、この場合、第1のスレッドは第2のデッドロックを待っているからです。 –

+0

は妥当な音です。デッドロックの問題は、[MSDNページ](http://msdn.microsoft.com/en-us/library/k9x6w0hc.aspx)では明らかではありませんが、[c#specification 17.11](http://www.ecma -international.org/publications/files/ECMA-ST/Ecma-334.pdf)は、少なくとも「与えられたアプリケーションドメイン内に最大で1回」注意することによってヒントを与えます。 –

0

静的メソッドが必ずしもスレッドセーフではない理由についての補足として、なぜそうであるのか、なぜそうであるのかを検討する価値があります。それは上のコードに影響を与えるために方法がないので、この純粋な関数はスレッドセーフである

public static int Max(int x, int y) 
{ 
    return x > y ? x : y; 
} 

:彼らはあるかもしれない理由

第一の理由は、私が思うに、あなたが考えていた例一種であります他のスレッドでは、ローカルxyは、共有場所に保存されず、共有場所に格納されず、デリゲートでキャプチャされず、純粋にローカルコンテキストを残しません。

スレッドセーフな操作の組み合わせは、スレッドセーフではないことに注意してください(たとえば、並行辞書にキーがあり、そのスレッドセーフな値の読み取りがスレッドセーフであるかどうかこれら2つのスレッドセーフな操作の間で状態が変わる可能性があるため、スレッドセーフではありません)。静的メンバーは、これを避けるために、スレッドセーフでない方法で結合できるメンバーではありません。

静的メソッドはまた、それ自身のスレッドの安全性を保証することがあります。

public object GetCachedEntity(string key) 
{ 
    object ret; //local and remains so. 
    lock(_cache) //same lock used on every operation that deals with _cache; 
    return _cache.TryGetValue(key, out ret) ? ret : null; 
} 

OR:もちろん

public object GetCachedEntity(string key) 
{ 
    object ret; 
    return _cache.TryGetValue(key, out ret) ? ret : null; //_cache is known to be thread-safe in itself! 
} 

を、ここで、これは他の腐敗を防止するために自分自身を保護し、インスタンスメンバーよりも違いはありません(共有するオブジェクトを扱う他のすべてのコードと協調して)

静的メンバーはスレッドセーフであり、インスタンスメンバーはスレッドセーフではないことは非常に一般的です。ほとんどすべての静的メンバはドキュメント内でスレッドの安全性を保証しています。ほとんどのインスタンスメンバは、(インスタンスメンバが実際にスレッドセーフである場合でも)同時使用のために特別に設計されたクラスを禁止していません。

理由は2つある:

  1. 最も一般的に有用な静的メンバーの操作の一種であり、純粋な機能(例えば、Mathクラスの静的メンバのほとんど)または静的読み出しを読み取ります他のスレッドによって変更されない変数のみ。

  2. サードパーティの静的メンバーに自分の同期を持たせることは非常に困難です。

第2の点は重要です。インスタンスメンバーがスレッドセーフではないオブジェクトがある場合、呼び出しが異なるインスタンス間で共有されるスレッドセーフではないデータに影響しないと仮定すると(可能ですが、ほぼ確実に悪い設計です)、次に共有したい場合スレッド、私は自分のロックを提供することができます。

しかし、スレッドセーフではない静的メンバーを扱っている場合は、これを行うのがはるかに難しいです。確かに、自分のコードだけでなく、他の当事者のコードで競争するかもしれないと考えると、不可能かもしれません。これは、そのようなパブリックな静的メンバーを役に立たないものにするでしょう。

静的メンバーがスレッドセーフである傾向がある理由は、(純粋な機能をカバーしていますが)静的メンバーがスレッドセーフである傾向があるということではありませんが、難しくなります。だから実際には、コードの作成者はユーザーのためにそれをやる必要があります。なぜなら、ユーザーは自分自身ができないからです。

関連する問題