我重写了最初只用于一个线程的方法,使其可用于多个线程。现在,此方法接受两个并发的集合:ConcurrentBag
,它是List
;和ConcurrentQueue
,它是Queue
。
目的是匹配两个集合中的两个标题并做出一些逻辑,这是在ConcurrentBag
项目中进行简单的值分配。我肯定知道ConcurrentBag
中的所有符号都在ConcurrentQueue
中。
当我为多线程编写此代码时,发生了一些标题不匹配(〜20%)的情况,而在线程上时则没有发生。只有在调试期间,我才能匹配这些标题,然后分配值。迭代这两个集合必须有一些问题。也许在同一时间,许多线程从同一项目读取值,但仅读取应该不会有问题吗?
下面的代码:
public void UpdateWithPercent(ref ConcurrentBag<Book> refList, ConcurrentQueue<Book> list)
{
var size = list.Count;
int numProcs = Environment.ProcessorCount;
var divider = CalculatBiggestDivider(size);
var nextIteration = 0;
var remainingWork = numProcs;
var internalRefList = refList;
using (ManualResetEvent mre = new ManualResetEvent(false))
{
for (int i = 0; i < numProcs; i++)
{
ThreadPool.QueueUserWorkItem(delegate
{
IEnumerable<Book> partialList;
while (-(nextIteration - Interlocked.Add(ref nextIteration, (partialList = DequeueChunk(list, divider)).Count()))> 0)
{
foreach (var item in partialList)
{
foreach (var x in internalRefList)
{
if (x.Title == item.Title)
{
x.Orders += item.Orders;
break;
}
};
}
}
if (Interlocked.Decrement(ref remainingWork) == 0)
{
mre.Set();
}
});
}
mre.WaitOne();
}
refList = internalRefList;
}
private int CalculatBiggestDivider(int count)
{
var divider = 1;
for (int i = 30; i > 0; i--)
{
if (count % i == 0)
{
divider = i;
break;
}
}
return divider;
}
private IEnumerable<T> DequeueChunk<T>(ConcurrentQueue<T> queue, int chunkSize)
{
for (int i = 0; i < chunkSize && queue.Count > 0; i++)
{
T item;
bool success = queue.TryDequeue(out item);
if (!success)
{
i = chunkSize;
continue;
}
yield return item;
}
}
答案 0 :(得分:0)
最终,我决定退出nestedloops,并使用ConcurrentDictionary
。现在可以了。
public void UpdateWithPercent(ref ConcurrentDictionary<string, Book> refList, List<Book> list, int ticker, int maxTimes)
{
var size = list.Count;
int numProcs = Environment.ProcessorCount;
var divider = CalculatBiggestDivider(size);
var nextIteration = 0;
var remainingWork = numProcs;
var internalRefList = refList;
using (ManualResetEvent mre = new ManualResetEvent(false))
{
for (int i = 0; i < numProcs; i++)
{
ThreadPool.QueueUserWorkItem(delegate
{
int index = 0;
while ((index = Interlocked.Add(ref nextIteration, divider) - divider) < size)
{
foreach (var item in list.GetRange(index, divider))
{
Book x;
if (internalRefList.TryGetValue(item.Title, out x))
{
x.Orders += item.Orders;
}
};
}
if (Interlocked.Decrement(ref remainingWork) == 0)
{
mre.Set();
}
});
}
mre.WaitOne();
}
refList = internalRefList;
}