我正在寻找有关并发写入到System.Collections.BitArray类的线程安全的信息。
具体而言,请考虑以下人为的示例:
BitArray bits = new BitArray(1000000);
Parallel.For(0, bits.Count, i =>
{
bits[i] = i % 3 == 0;
});本能地告诉我,如果两个线程试图写入位数组的相同底层整数值,并发未锁定访问将产生不正确的结果,但我无法找到支持它的任何证据,而且在运行时也没有遇到任何问题。
这是个安全的行动吗?
如果没有,为什么我没有看到这段代码失败或产生不正确的输出?
更新
经过进一步的测试,我认为下面的测试证明在本例中使用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
}正如我前面提到的,我怀疑原因是BitArray中的32个值共享数组的相同整数值。
这个逻辑正确吗?
出于问题的考虑,请假定除了代码中显示的线程之外,没有其他线程正在访问集合。
发布于 2012-09-10 09:32:48
查看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-10 09:13:01
我认为在BitArray下引用MSDN的话应该告诉您您想知道的一切:
此实现不为BitArray提供同步(线程安全)包装。 通过集合枚举的本质上不是一个线程安全过程。即使在同步集合时,其他线程仍然可以修改集合,这将导致枚举数抛出异常。要确保枚举期间的线程安全,可以在整个枚举期间锁定集合,或者捕获其他线程所做更改导致的异常。
我已经大胆地说出了重要的一点。通过集合进行的任何枚举都不是线程安全的,因此对元素的任何更改也不是线程安全,您应该锁定整个集合或使用线程安全集合之一。(虽然我不确定是否存在BitArray )
发布于 2014-10-12 00:03:35
我编写了一个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);
}
}
}
}https://stackoverflow.com/questions/12348489
复制相似问题