3

私の C# の理解では、(Jeff Richter と Jon Skeet のおかげで) 代入は「アトミック」であると言えます。そうでないのは、読み取りと書き込み (インクリメント/デクリメント) が混在しているため、Interlocked でメソッドを使用する必要がある場合です。読み取りと割り当てのみの場合、両方の操作はアトミックになりますか?

public class Xyz
{
    private volatile int _lastValue;
    private IList<int> AvailableValues { get; set; }
    private object syncRoot = new object();
    private Random random = new Random();

    //Accessible by multiple threads
    public int GetNextValue() //and return last value once store is exhausted
    {
        //...

        var count = 0;
        var returnValue = 0;

        lock (syncRoot)
        {
            count = AvailableValues.Count;
        }

        if (count == 0)
        {
            //Read... without locking... potential multiple reads
            returnValue = _lastValue;
        }
        else
        {

            var toReturn = random.Next(0, count);

            lock (syncRoot)
            {
                returnValue = AvailableValues[toReturn];
                AvailableValues.RemoveAt(toReturn);
            }
            //potential multiple writes... last writer wins
            _lastValue = returnValue;
         }

        return returnValue;

    }

4

6 に答える 6

18

私の C# の理解では、(Jeff Richter と Jon Skeet のおかげで) 代入は「アトミック」であると言えます。

一般に、代入はアトミックではありません。C# 仕様では、アトミックであることが保証されているものを注意深く呼び出しています。セクション 5.5 を参照してください。

次のデータ型の読み取りと書き込みはアトミックです: bool、char、byte、sbyte、short、ushort、uint、int、float、および参照型。さらに、前のリストの基になる型を持つ列挙型の読み取りと書き込みもアトミックです。long、ulong、double、decimal などの他の型、およびユーザー定義型の読み取りと書き込みは、atomic であるとは限りません

(強調を追加しました。)

読み取りと割り当てのみの場合、両方の操作はアトミックになりますか?

繰り返しますが、セクション 5.5 であなたの質問に答えます。

アトミックな読み取り-変更-書き込みの保証はありません

于 2009-07-08T15:10:36.360 に答える
11

volatile実際には(レジスタなどで)キャッシングに関連しています。volatileその値が実際にすぐにメモリに書き込まれたり、メモリから読み取られたりすることがわかっています(実際には、そうでない場合は常にそうではありません)。これにより、異なるスレッドが互いの更新をすぐに確認できます。命令の並べ替えには他にも微妙な問題がありますが、それは複雑になります。

ここで考慮すべき「アトミック」の意味は 2 つあります。

  • それ自体が単一の読み取りアトミック/書き込みアトミックです (つまり、別のスレッドが 2 つの異なる 2 つDoubleの半分を取得し、実際には存在しなかった数値を生成する可能性があります)。
  • アトミック/分離された読み取り/書き込みペアです

「それ自体」は値のサイズによって異なります。1回の操作で更新できますか?読み取り/書き込みのペアは、分離と関係があります。つまり、更新が失われるのを防ぎます。

あなたの例では、2 つのスレッドが同じ を読み取り_lastValue、両方が計算を行ってから (別々に) update することが可能です_lastValue。それらの更新の 1 つが失われます。実際には、読み取り/書き込みプロセスlock期間中にが必要になると思います。

于 2009-07-08T12:39:41.913 に答える
5

volatile キーワードを使用しても、アクセスがスレッドセーフになるわけではありません。変数の読み取りが、以前の読み取りからキャッシュされたレジスターから読み取られる可能性があるのではなく、メモリから読み取られるようにするだけです。特定のアーキテクチャではこの最適化が行われ、複数のスレッドが同じ変数に書き込みを行っている場合に古い値が使用される可能性があります。

アクセスを適切に同期するには、より広いロックが必要です。

public class Xyz
{
    private volatile int _lastValue;
    private IList<int> AvailableValues { get; set; }
    private object syncRoot = new object();
    private Random rand = new Random();

    //Accessible by multiple threads
    public int GetNextValue() //and return last value once store is exhausted
    {
        //...

        lock (syncRoot)
        {
            var count = AvailableValues.Count;
            if(count == 0)
                return _lastValue;

            toReturn = rand.Next(0, count);
            _lastValue = AvailableValues[toReturn];
            AvailableValues.RemoveAt(toReturn);
        }
        return _lastValue;
    }
}

パフォーマンスが懸念される場合は、O(1) 削除操作がサポートされているため、AvailableValues に LinkedList を使用することを検討してください。

于 2009-07-08T12:37:55.533 に答える
2

.Net 2.0 以前の場合、ReaderWriterLockというクラスがあり、書き込みと読み取りを別々にブロックできます。役立つかもしれません。

.Net 3.5以降では、Microsoft が次のように説明しているReaderWriterLockSlimを検討してください。

ReaderWriterLockSlim は ReaderWriterLock に似ていますが、再帰およびロック状態のアップグレードとダウングレードのルールが単純化されています。ReaderWriterLockSlim は、潜在的なデッドロックの多くのケースを回避します。さらに、ReaderWriterLockSlim のパフォーマンスは、ReaderWriterLock よりも大幅に優れています。ReaderWriterLockSlim は、すべての新規開発に推奨されます。

于 2009-07-08T13:01:12.610 に答える
0

これ (原子性) は保証されません。

于 2009-07-08T12:35:35.970 に答える
0

それらはいくつかのタイプのリンク用です。あなたの場合、それは int であるため、C#仕様によればアトミックです。ただし、このトピックの他のユーザーと同様に、コードがスレッドセーフであることを保証するものではありません。

于 2009-07-08T12:39:57.513 に答える