我试图从ConcurrentBag
一举夺取所有物品。由于该集合上的TryEmpty
没有任何内容,我已采用与此处所述相同的方式使用Interlocked.Exchange
:How to remove all Items from ConcurrentBag?
我的代码如下所示:
private ConcurrentBag<Foo> _allFoos; //Initialized in constructor.
public bool LotsOfThreadsAccessingThisMethod(Foo toInsert)
{
this._allFoos.Add(toInsert);
return true;
}
public void SingleThreadProcessingLoopAsALongRunningTask(object state)
{
var token = (CancellationToken) state;
var workingSet = new List<Foo>();
while (!token.IsCancellationRequested)
{
if (!workingSet.Any())
{
workingSet = Interlocked.Exchange(ref this._allFoos, new ConcurrentBag<Foo>).ToList();
}
var processingCount = (int)Math.Min(workingSet.Count, TRANSACTION_LIMIT);
if (processingCount > 0)
{
using (var ctx = new MyEntityFrameworkContext())
{
ctx.BulkInsert(workingSet.Take(processingCount));
}
workingSet.RemoveRange(0, processingCount);
}
}
}
问题是这有时会遗漏添加到列表中的项目。我已经编写了一个测试应用程序,可以将数据提供给我的ConcurrentBag.Add
方法并验证它是否正在发送所有数据。当我在Add
电话上设置断点并检查后ConcurrentBag
的计数时,它为零。该项目尚未添加。
我之所以相当积极,因为Interlocked.Exchange
来电并没有使用ConcurrentBag
的内部锁定机制,因此它在某处丢失了数据在交换中,但我不知道实际发生了什么。
如何在不使用我自己的锁定机制的情况下,一次从ConcurrentBag
中抓取所有项目?为什么Add
会忽略该项?
答案 0 :(得分:1)
我认为不需要从ConcurentBag
中获取所有项目。您可以通过如下更改处理逻辑来实现与您尝试实现的完全相同的行为(不需要自己的同步或互锁交换):
public void SingleThreadProcessingLoopAsALongRunningTask(object state)
{
var token = (CancellationToken)state;
var buffer = new List<Foo>(TRANSACTION_LIMIT);
while (!token.IsCancellationRequested)
{
Foo item;
if (!this._allFoos.TryTake(out item))
{
if (buffer.Count == 0) continue;
}
else
{
buffer.Add(item);
if (buffer.Count < TRANSACTION_LIMIT) continue;
}
using (var ctx = new MyEntityFrameworkContext())
{
ctx.BulkInsert(buffer);
}
buffer.Clear();
}
}