我的情况是我有生产者/消费者情景。制作人永远不会停止,这意味着即使有时间BC中没有项目,也可以在以后添加更多项目。
从.NET Framework 3.5迁移到4.0,我决定使用BlockingCollection
作为使用者和生产者之间的并发队列。我甚至添加了一些并行扩展,因此我可以将BC与Parallel.ForEach
一起使用。
问题在于,在消费者线程中,我需要一种混合模型:
Parallel.ForEach(bc.GetConsumingEnumerable(), item => etc
foreach
中,我执行彼此之间不相互依赖的所有任务。伪代码中的一个小例子如下:
生产者:
//This event is triggered each time a page is scanned. Any batch of new pages can be added at any time at the scanner
private void Current_OnPageScanned(object sender, ScannedPage scannedPage)
{
//The object to add has a property with the sequence number
_concurrentCollection.TryAdd(scannedPage);
}
消费者:
private void Init()
{
_cancelTasks = false;
_checkTask = Task.Factory.StartNew(() =>
{
while (!_cancelTasks)
{
//BlockingCollections with Paralell ForEach
var bc = _concurrentCollection;
Parallel.ForEach(bc.GetConsumingEnumerable(), item =>
{
ScannedPage currentPage = item;
// process a batch of images from the bc and check if an image has a valid barcode. T
});
//Here should go the code that takes the results from each tasks, process them in the same FIFO order in which they entered the BC and save each image to a file, all of this in this same thread.
}
});
}
显然,这不能正常工作,因为.GetConsumingEnumerable()
阻止,直到BC中有另一个项目。我认为我可以完成任务,只需在同一批次中激活4或5个任务,但是:
TryTake
所以如果没有什么可以采取它们不会阻止,因为我不知道我是否可以永远达到来自BC的项目数量作为一批任务,例如,BC中剩下的一个项目和一批4个任务)? 答案 0 :(得分:2)
我想我会按照您的要求进行操作,为什么不创建一个ConcurrentBag并在处理时加入它:
while (!_cancelTasks)
{
//BlockingCollections with Paralell ForEach
var bc = _concurrentCollection;
var q = new ConcurrentBag<ScannedPage>();
Parallel.ForEach(bc.GetConsumingEnumerable(), item =>
{
ScannedPage currentPage = item;
q.Add(item);
// process a batch of images from the bc and check if an image has a valid barcode. T
});
//Here should go the code that takes the results from each tasks, process them in the same FIFO order in which they entered the BC and save each image to a file, all of this in this same thread.
//process items in your list here by sorting using some sequence key
var items = q.OrderBy( o=> o.SeqNbr).ToList();
foreach( var item in items){
...
}
}
这显然不会按照它们添加到BC的确切顺序排列它们,但是您可以像Alex建议的那样向ScannedPage对象添加一些序列nbr,然后对结果进行排序。
以下是我处理序列的方法:
将其添加到ScannedPage
类:
public static int _counter; //public because this is just an example but it would work.
获取序列nbr并在此处分配:
private void Current_OnPageScanned(object sender, ScannedPage scannedPage)
{
lock( this){ //to single thread this process.. not necessary if it's already single threaded of course.
System.Threading.Interlocked.Increment( ref ScannedPage._counter);
scannedPage.SeqNbr = ScannedPage._counter;
...
}
}