web-dev-qa-db-ja.com

メソッドをスレッドセーフにする理由ルールは何ですか?

メソッドをスレッドセーフにするための全体的なルール/ガイドラインはありますか?おそらく100万回の一時的な状況があることを理解していますが、一般的にはどうですか?これは簡単ですか?

  1. メソッドがローカル変数にのみアクセスする場合、スレッドセーフです。

それですか?それは静的メソッドにも当てはまりますか?

@Cybisから提供された1つの答えは次のとおりです。

各スレッドは独自のスタックを取得するため、ローカル変数はスレッド間で共有できません。

それは静的メソッドにも当てはまりますか?

メソッドに参照オブジェクトが渡された場合、スレッドの安全性が損なわれますか?私はいくつかの研究を行ってきましたが、特定のケースについて多くのことがありますが、いくつかのルールを使用して、メソッドがスレッドセーフであることを確認するためのガイドラインを定義できるようにしたいと考えていました。

ですから、私の究極の質問は、「スレッドセーフなメソッドを定義するルールの短いリストはありますか?もしそうなら、それらは何ですか?」です。

編集
ここでは多くの良い点が指摘されています。この質問に対する本当の答えは、「スレッドの安全性を確保するための簡単なルールはない」と思います。クール。いいよしかし、一般的に受け入れられた答えは良い、短い要約を提供すると思います。常に例外があります。だからそれ。私はそれで生きることができます。

141
Bob Horn

メソッド(インスタンスまたは静的)がそのメソッド内でスコープされた変数のみを参照する場合、各スレッドには独自のスタックがあるため、スレッドセーフです。

この場合、複数のスレッドがThreadSafeMethodを問題なく同時に呼び出すことができます。

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

これは、ローカルスコープの変数のみを参照する他のクラスメソッドをメソッドが呼び出す場合にも当てはまります。

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

メソッドが(オブジェクト状態)プロパティまたはフィールド(インスタンスまたは静的)にアクセスする場合、ロックを使用して、値が別のスレッドによって変更されないようにする必要があります。

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

メソッドに渡される構造体でも不変でもないパラメーターは、メソッドのスコープ外の別のスレッドによって変更される可能性があることに注意してください。

適切な同時実行性を確保するには、ロックを使用する必要があります。

詳細については、 lock statement C#reference および ReadWriterLockSlim を参照してください。

lockは、一度に1つの機能を提供するのに最も役立ちます。
ReadWriterLockSlimは、複数のリーダーとシングルライターが必要な場合に便利です。

129
Trevor Pilley

メソッドがローカル変数にのみアクセスする場合、スレッドセーフです。それですか?

絶対にありません。スレッドセーフではない単一のスレッドからアクセスされる単一のローカル変数のみでプログラムを作成できます。

https://stackoverflow.com/a/8883117/88656

それは静的メソッドにも当てはまりますか?

絶対違う。

@Cybisが提供する1つの答えは、「各スレッドが独自のスタックを取得するため、スレッド間でローカル変数を共有することはできません。」でした。

絶対違う。ローカル変数の際立った特徴は、それがローカルスコープ内からのみ表示であることであり、一時プールに割り当てられているであるということではありません。 2つの異なるスレッドから同じローカル変数にアクセスすることは完全に合法であり、可能です。匿名メソッド、ラムダ、イテレータブロックまたは非同期を使用してアクセスできます。メソッド。

それは静的メソッドにも当てはまりますか?

絶対違う。

メソッドに参照オブジェクトが渡された場合、スレッドの安全性が損なわれますか?

多分。

私はいくつかの研究を行ってきましたが、特定のケースについて多くのことがありますが、いくつかのルールを使用して、メソッドがスレッドセーフであることを確認するためのガイドラインを定義できるようにしたいと考えていました。

あなたは失望して生きることを学ぶ必要があります。これは非常に難しい課題です。

ですから、私の究極の質問は、「スレッドセーフなメソッドを定義するルールの短いリストはありますか?

いや。前の例で見たように空のメソッドはスレッドセーフではありません。 「メソッドがcorrect "であることを保証するルールの短いリストはありますか」と尋ねることもできます。いいえ、ありません。スレッドセーフは、非常に複雑な種類の正確性にすぎません。

さらに、あなたが質問をしているという事実は、スレッドの安全性に関する基本的な誤解を示しています。スレッドセーフティは、プログラムのlocalプロパティではなく、globalです。正しいことが難しいのは、その安全性を確保するためにプログラム全体のスレッド動作について完全な知識が必要であるためです。

繰り返しますが、私の例を見てください:すべての方法は簡単です。プログラムがデッドロックになるのは、メソッドが「グローバル」レベルで相互にやり取りする方法です。すべての方法を見て「安全」としてチェックオフし、プログラム全体が安全であると期待することはできません。あなたの家は100%の非中空レンガで作られているため、その家も非中空。家の空洞性は、全体の全体的な特性であり、その部分の特性の集合ではありません。

102
Eric Lippert

厳格なルールはありません。

以下に、.NETでコードスレッドを安全にするためのいくつかのルールと、これらが良いルールではない理由を示します。

  1. 関数およびそれが呼び出すすべての関数は、純粋で(副作用がない)、ローカル変数を使用する必要があります。これによりコードはスレッドセーフになりますが、.NETでこの制限を使用して実行できる興味深いことはほとんどありません。
  2. 共通のオブジェクトを操作するすべての関数は、共通のものをlockしなければなりません。すべてのロックは同じ順序で実行する必要があります。これにより、コードはスレッドセーフになりますが、非常に遅くなり、複数のスレッドを使用しないこともできます。
  3. ...

コードスレッドを安全にするルールはありません。できることは、コードがアクティブに実行されている回数に関係なくコードが動作することを確認することだけです。各スレッドは、各スレッドが独自の状態/場所、およびこれは、共通オブジェクトにアクセスしている各関数(静的またはそれ以外)に対して。

9
earlNameless

オブジェクトロック、ステートレス、または不変を使用して同期する必要があります。

リンク: http://docs.Oracle.com/javase/tutorial/essential/concurrency/immutable.html

4
kasavbere