6

get および set コードを実装しなくてもメンバーを簡単に作成できる C# でクラスを作成する簡単な方法が気に入っていますが、(私の知る限り)それらはスレッドセーフではありません!

public class SocksEntry
{
    public int Int1 { get; set; }
    public int Int2 { get; set; }
}

C# は、これを行うことなくスレッド セーフを追加する簡単な方法を提供していますか?

public class SocksEntry
{
    protected object _lock = new object();

    // internal
    private int _int1 = 0;
    private int _int2 = 0;

    // accessors
    protected int Int1
    {
        get { lock (_lock) return _int1; }
        set { lock (_lock) _int1 = value; }
    }

    protected int Int2
    {
        get { lock (_lock) return _int2; }
        set { lock (_lock) _int2 = value; }
    }
}

スレッドセーフではないバージョンに比べて、明らかにクラス全体が大きくなり、作成するのが面倒になります!

4

5 に答える 5

7

クラススレッドを安全にすることは、想像するよりもはるかに困難です。ゲッターとセッターの周りにロックをかけるだけでは不十分かもしれません。したがって、ベストプラクティスは、クラスをスレッドセーフではなく、スレッドセーフが必要な場合は、このクラスのコンシューマーにアクセスを同期する責任を任せることです。コンシューマーがスレッドセーフを必要としない場合は、すばらしいです。スレッドセーフを必要とせずにクラスに組み込んだからといって、アプリケーションのパフォーマンスに悪影響を与えることはありません。.NETのクラスの99.99%がスレッドセーフではないと思うのはなぜですか?

于 2012-12-26T15:15:58.693 に答える
1

一言で言えば、いいえ。

自動実装されたプロパティは素晴らしいですが、制限に達したときはそれだけです。

ただし、ボイラープレートコードを作成するVisual Studioスニペット(のような)を作成することはできます。prop

于 2012-12-26T15:17:44.107 に答える
0

あなたが言及しているスレッドセーフの問題は次のようなものだと思います。

public int Value { get; set; }

public void Foo()
{
    if (this.Value > 0)   // Read the property and make a decision based on its present value
    {
        // Do something with the property, assuming its value has passed your test.
        // Note that the property's value may have been changed by another thread between the previous line and this one.
        Console.WriteLine(this.Value);
    }
}

問題は、プロパティ値を調べたときと使用したときの間で変更されている可能性があることです。これは、自動プロパティ、バッキング変数、または質問にあるロックのいずれを使用する場合でも発生します。正しい解決策は、アプリケーションがどのように動作する必要があるかによって異なります(値が1つの行と別の行の間で変更された場合はどうすればよいですか)。一般的な解決策は、値を使用している関数に値をキャッシュすることです。

public int Value { get; set; }

public void Foo()
{
    int cachedValue = this.Value;

    if (cachedValue  > 0)
    {
        Console.WriteLine(cachedValue );
    }
}

ただし、それはアプリが行うことになっていることに対して必ずしも正しいとは限りません。

于 2012-12-26T15:19:44.733 に答える
0

最も簡単で、多くの場合、唯一の正しいアプローチは、クラスをimmutableにすることです。そのため、読み取り操作のみを使用できます。

public sealed class SocksEntry
{
    public int Int1 { get; private set; }
    public int Int2 { get; private set; }
}

しかし、特定のビジネス エンティティでこれが不可能な場合 (プロパティ セッターではなくいくつかのビジネス メソッドを公開する場合) は、各プロパティ セッターではなく、メソッド全体 (トランザクションを考えてください) を同期する方がはるかに簡単です。

// lock entire business transaction if possible
public void UpdateEntity(int a, int b)
{
   lock (entityLock)
   {
       Int1 = a;
       Int2 = b;
    }
}    
于 2012-12-26T15:49:27.270 に答える
0

.NET メモリ モデルでは、プラットフォームのポインターのサイズ以下のネイティブ整数型の読み取りと書き込み (つまりint、32 ビット、long64 ビット) はアトミックです。アトミックとは、ロックを使用せずに読み書きできることを意味し、不完全な値 (つまり、書き込みの中間) を読み書きすることはありません。

ただし、.NET は、書き込みが他のスレッドにすぐに表示されることを保証しません (つまり、スレッド A で書き込み、次にスレッド B で読み取り、古い値を読み取ることができます)。x86 では、アーキテクチャをすぐに確認できますが、他のプラットフォームでは、volatileフィールドまたはThread.MemoryBarrier書き込み後に使用する必要があります。lockメモリバリアが挿入されるので、心配する必要はありません。

考えるべきもう 1 つのことは、クラスの使用方法です。1 回のアクセスはアトミックですが、lock複数のフィールドを 1 つのアトミック操作として読み書きする場合は、依然として が必要です。(まあ、技術的に常にというわけではありません - 時々実行できる特定の最適化がありますが、学習中は単純なものに固執してください)

要するに、lockかなりやり過ぎです。すぐに可視性が必要な場合は、それを取り除き、揮発性/バリアを使用できます。

于 2012-12-26T15:50:09.203 に答える