我已经实现了一个简单的任务来创建一个允许并发写入的固定大小的列表,并且可以随时转储列表中项目的最新快照。
这是我的实施。对于每个线程,偏移量将原子地增加,并且如果达到列表的大小则重置。不同的线程应该对数组的每个部分进行隔离访问。
我的问题是当我调用Dump()时,前几个项目没有存储在列表中。此外,是否存在可以同时执行原子增加和重置的互锁功能,因此我不必创建锁定器对象和锁定块?感谢。
public static void Main(string[] args)
{
ConcurrentCircularFixedList<int> list = new ConcurrentCircularFixedList<int>(20);
Enumerable.Range(1, 30).AsParallel().Select(nu => list.Enqueu(nu)).ToList();
}
public class ConcurrentCircularFixedList<T>
{
private int _size;
private int _offset;
private sealed object _locker = new Object();
privateT[] _list;
public ConcurrentCircularFixedList(int size)
{
_size = size;
_offset = 0;
_list = new T[_size];
}
public int Enqueu(T item)
{
_list[_offset] = item;
lock(_locker)
{
Debug.Write("B " + _offset);
_offset += 1;
if(_offset == _size)
_offset = 0;
Debug.Write("A " + _offset + "\n");
}
return _offset;
}
public T[] Dump()
{
return _list.ToArray();
}
}
答案 0 :(得分:2)
这是一个小型无锁列表,可以在写入时进行复制。在使用之前应该清楚地了解性能特征。当你有很多作家或者名单很大时,它很昂贵。读取是无同步的,因为列表实际上是不可变的。当然,这可以通过各种方式得到改善,但你明白了。实际上,它牺牲了一些内存压力和较慢的写入以实现零成本读取。
public class CopyWriteList<T>
{
private volatile List<T> list;
public CopyWriteList()
{
list = new List<T>();
}
public CopyWriteList(int capacity)
{
list = new List<T>(capacity);
}
public T this[int index]
{
get { return list[index]; }
set { Replace(x => x[index] = value); }
}
public void Clear()
{
Replace(x => x.Clear());
}
public void Add(T item)
{
Replace(x => x.Add(item));
}
//Etc....
private void Replace(Action<List<T>> action)
{
List<T> current;
List<T> updated;
do
{
current = list;
updated = new List<T>(current);
action(updated);
} while (Interlocked.CompareExchange(ref list, updated, current) != current);
}
public List<T> GetSnapshot()
{
return list;
}
}
或者这是您的代码的固定版本。请注意,读者和作者之间存在争用。性能可能因此受到影响(就像昂贵的上下文切换一样)。
public class ConcurrentCircularFixedList<T>
{
private readonly int _size;
private int _offset;
private readonly object _locker = new Object();
private readonly T[] _list;
public ConcurrentCircularFixedList(int size)
{
_size = size;
_offset = 0;
_list = new T[_size];
}
public int Enqueue(T item)
{
lock (_locker)
{
_list[_offset] = item;
Debug.Write("B " + _offset);
_offset += 1;
if (_offset == _size)
_offset = 0;
Debug.Write("A " + _offset + "\n");
return _offset;
}
}
public T[] Dump()
{
lock (_locker)
return _list.ToArray();
}
}