C# 位阵列线程安全

C# 位阵列线程安全,c#,parallel-processing,thread-safety,bitarray,C#,Parallel Processing,Thread Safety,Bitarray,我正在寻找有关并发写入System.Collections.BitArray类的线程安全的信息 具体而言,考虑下面的设计实例: BitArray bits = new BitArray(1000000); Parallel.For(0, bits.Count, i => { bits[i] = i % 3 == 0; }); 本能告诉我,如果两个线程试图写入bitarray的相同基础整数值,那么并发解锁访问将产生错误的结果,但我找不到任何证据支持它,并且在运行时也没有遇到任何问

我正在寻找有关并发写入
System.Collections.BitArray
类的线程安全的信息

具体而言,考虑下面的设计实例:

BitArray bits = new BitArray(1000000);

Parallel.For(0, bits.Count, i =>
{
    bits[i] = i % 3 == 0;
});
本能告诉我,如果两个线程试图写入bitarray的相同基础整数值,那么并发解锁访问将产生错误的结果,但我找不到任何证据支持它,并且在运行时也没有遇到任何问题

这是一次安全的手术吗

如果不是,为什么我没有看到这个代码失败或产生不正确的输出

更新 在进一步测试之后,我认为下面的测试证明了在本例中使用
BitArray
不是线程安全的

另一方面,使用
bool[]
似乎是安全的

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
}
专用静态布尔比较器阵列(位阵列a、位阵列b)
{
如果(a.Count!=b.Count)返回false;
for(int i=0;i
{
位[i]=bools[i]=i%3==0;
});
//从布尔数组创建一个位数组
BitArray boolBits=新的位数组(bools);
//检查它们是否包含正确的值
bool-isBitArrayCorrect=CompareBitArrays(控制组,位);//FALSE
bool isBoolArrayCorrect=CompareBitArrays(控制组,boolBits);//真
}
正如我提到的,我怀疑原因是
位数组中的32个值共享数组的相同整数值

这个逻辑正确吗


出于问题的考虑,请假设除代码中显示的线程外,没有其他线程正在访问该集合。

我认为MSDN下面的这段引用应该告诉您您想要知道的一切:

此实现不提供同步的(线程安全的) 位数组的包装器

通过集合枚举本质上不是线程安全的 程序。即使在同步集合时,其他线程也可以 仍然修改集合,这会导致枚举数抛出 例外。要保证枚举期间的线程安全,可以 在整个枚举过程中锁定集合或捕获 由其他线程所做的更改导致的异常


我把重要的一点加粗了。通过集合的任何枚举都不是线程安全的,因此元素的任何更改也不是线程安全的,您应该锁定整个集合或使用其中一个线程安全集合。(虽然我不确定是否存在位数组)

查看
位数组。设置方法代码:

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
}
公共无效集(整数索引,布尔值)
{
如果(索引<0 | |索引>=此.Length)
{
抛出新ArgumentOutOfRangeException(“index”,Environment.GetResourceString(“ArgumentOutOfRange_index”);
}
如果(值)
{

这个.m_array[index/32]|=1我写了一个ThreadSafeBitArray类,它的性能比普通的锁定BitArray类要好。也许有人会发现它很有用

public class ThreadSafeBitArray
{
    private static int[] _cachedShifts;

    static ThreadSafeBitArray()
    {
        _cachedShifts = new int[32];

        for (int index = 0; index < 32; index++)
        {
            _cachedShifts[index] = ((int)1 << index);
        }
    }

    private int _length;
    private int[] _arr;

    public ThreadSafeBitArray(int length)
    {
        _length = length;
        _arr = new int[ToUnderlineLength(length)];
    }

    private int ToUnderlineLength(int length)
    {
        int underlineLength = length / 32;

        if (length % 32 != 0)
        {
            underlineLength++;
        }

        return underlineLength;
    }

    public int Length
    {
        get { return _length; }
    }

    public bool this[int index]
    {
        get
        {
            return (Interlocked.CompareExchange(ref _arr[index >> 5], 0, 0) & (_cachedShifts[index & 31])) != 0;
        }
        set
        {
            int prevValue;

            if (value)
            {
                do
                {
                    prevValue = Interlocked.CompareExchange(ref _arr[index >> 5], 0, 0);
                }
                while (Interlocked.CompareExchange(ref _arr[index >> 5], prevValue | (_cachedShifts[index & 31]), prevValue) != prevValue);
            }
            else
            {
                do
                {
                    prevValue = Interlocked.CompareExchange(ref _arr[index >> 5], 0, 0);
                }
                while (Interlocked.CompareExchange(ref _arr[index >> 5], prevValue & ~(_cachedShifts[index & 31]), prevValue) != prevValue);
            }
        }
    }
}
公共类ThreadSafeBitArray
{
私有静态int[]\u缓存移位;
静态ThreadSafeBitArray()
{
_CachedShift=新整数[32];
对于(int-index=0;index<32;index++)
{
_缓存移位[索引]=((int)1>5],0,0)和(_缓存移位[索引&31])!=0;
}
设置
{
int值;
如果(值)
{
做
{
prevValue=联锁。比较交换(参考arr[索引>>5],0,0);
}
while(interlocated.compareeexchange(ref _arr[index>>5],prevValue |(_cachedshift[index&31]),prevValue)!=prevValue);
}
其他的
{
做
{
prevValue=联锁。比较交换(参考arr[索引>>5],0,0);
}
while(interlocated.compareeexchange(参考arr[index>>5],prevValue&~(\u cachedshift[index&31]),prevValue)!=prevValue);
}
}
}
}

即使没有对集合进行任何修改,这是否真的相关?即没有删除或添加任何项。如果集合没有更改(不可变),我想您不应该担心线程安全。@Int3ὰ 据我所知,OP正在
并行中写入集合。For
循环((I%3)==0)的结果正在写入
位[I]
int集合。这是一个修改,需要同步。@Rotem:枚举集合不是线程安全的操作,因此如果您想安全起见,可以使用线程同步,因为您永远不知道其他线程可能同时对您的集合执行操作。我猜除非您是绝对的请确保集合不会被任何其他线程同时枚举,那么您就不需要同步。@Tony AFAIK这不是枚举。没有使用枚举器。您不认为位更改方法不安全吗?请参阅相关的更新测试。如何解释结果?@Rotem:bit changing method
BitArray.Set
是u吗nsafe,因为它使用共享状态(
\u version
字段)。这在我的帖子中。解释结果的原因是什么?我问,因为在你的回答中,你写的唯一不安全的部分是
\u version++
@Rotem:没错。因为没有
\u version