3

System.Collections.BitArrayクラスへの同時書き込みのスレッドセーフに関する情報を探しています。

具体的には、次の不自然な例を検討してください。

BitArray bits = new BitArray(1000000);

Parallel.For(0, bits.Count, i =>
{
    bits[i] = i % 3 == 0;
});

本能は、2 つのスレッドが bitarray の同じ基になる整数値に書き込もうとすると、同時にロックされていないアクセスが誤った結果を生成することを教えてくれますが、それをサポートする証拠を見つけることができず、実行時に問題に遭遇したことはありません。

これは安全な操作ですか?

そうでない場合、このコードが失敗したり、正しくない出力が生成されたりしないのはなぜですか?

アップデート

BitArrayさらにテストした結果、次のテストは、この例でa を使用することはスレッドセーフではないことを証明していると思います。

bool[]一方、a を使用することは安全なようです。

private static bool CompareBitArrays(BitArray a, BitArray b)
{
    if (a.Count != b.Count) return false;
    for (int i = 0; i < a.Count; i++)
    {
        if (a[i] != b[i]) return false;
    }
    return true;
}

static void Main(string[] args)
{
    int numElements = 1000000;

    //create single-threaded bitarray with certifiably correct values.
    BitArray controlGroup = new BitArray(numElements);
    for (int i = 0; i < numElements; i++)
    {
        controlGroup[i] = i % 3 == 0;
    }

    //create a BitArray and bool array of equal size and fill them using Parallel.For.
    BitArray bits = new BitArray(numElements);
    bool[] bools = new bool[numElements];

    Parallel.For(0, numElements, i =>
    {
        bits[i] = bools[i] = i % 3 == 0;
    });

    //Create a BitArray from the bool array
    BitArray boolBits = new BitArray(bools);

    //Check if they contain correct values
    bool isBitArrayCorrect = CompareBitArrays(controlGroup, bits); //FALSE
    bool isBoolArrayCorrect = CompareBitArrays(controlGroup, boolBits); //TRUE
}

前述したように、その理由BitArrayは、配列内の 32 個の値が同じ整数値を共有しているためだと思います。

このロジックは正しいですか?

質問のために、コードに示されているスレッド以外のスレッドがコレクションにアクセスしていないと仮定してください。

4

3 に答える 3

2

BitArrayの下のMSDNからのこの引用は、あなたが知りたいことすべてを教えてくれるはずだと思います。

この実装は、BitArrayの同期された(スレッドセーフな)ラッパーを提供しません。

コレクションを介して列挙することは、本質的にスレッドセーフな手順ではありません。コレクションが同期されている場合でも、他のスレッドがコレクションを変更する可能性があるため、列挙子は例外をスローします。列挙中のスレッドの安全性を保証するために、列挙全体でコレクションをロックするか、他のスレッドによって行われた変更に起因する例外をキャッチすることができます。

重要な部分を太字にしました。コレクションを介した列挙はスレッドセーフではないため、要素の変更もスレッドセーフではありません。コレクション全体をロックするか、スレッドセーフコレクションの1つを使用する必要があります。(BitArrayが存在するかどうかはわかりませんが)

于 2012-09-10T09:13:01.633 に答える
2

BitArray.Setメソッド コードを調べます。

public void Set(int index, bool value)
{
    if (index < 0 || index >= this.Length)
    {
        throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
    }
    if (value)
    {
        this.m_array[index / 32] |= 1 << index % 32;
    }
    else
    {
        this.m_array[index / 32] &= ~(1 << index % 32);
    }
    this._version++; // this is definitely thread-unsafe
}

コレクションメンバーを列挙せずにインデックスでアクセスする限り、私が目にする唯一のスレッドセーフでないコード行は最後の行this._version++;です。

しかし、それは存在するので、このコードはスレッドセーフでないと考えることができます。

于 2012-09-10T09:32:48.027 に答える