C#中的生产者/混合消费者使用4.0框架类和阻塞集合
我有一个生产者/消费者场景。制作人从不停止,这意味着即使有一段时间BC中没有项目,以后也可以添加更多的项目 从.NETFramework3.5到4.0,我决定使用C#中的生产者/混合消费者使用4.0框架类和阻塞集合,c#,multithreading,c#-4.0,concurrency,producer-consumer,C#,Multithreading,C# 4.0,Concurrency,Producer Consumer,我有一个生产者/消费者场景。制作人从不停止,这意味着即使有一段时间BC中没有项目,以后也可以添加更多的项目 从.NETFramework3.5到4.0,我决定使用BlockingCollection作为消费者和生产者之间的并发队列。我甚至添加了一些并行扩展,这样我就可以将BC与parallel.ForEach一起使用 问题是,在消费者线程中,我需要一种混合模型: 我总是检查业务连续性,以处理任何带有 Parallel.ForEach(bc.getconsumineGenumerable(),it
BlockingCollection
作为消费者和生产者之间的并发队列。我甚至添加了一些并行扩展,这样我就可以将BC与parallel.ForEach
一起使用
问题是,在消费者线程中,我需要一种混合模型:
Parallel.ForEach(bc.getconsumineGenumerable(),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.
}
});
}
显然,这无法正常工作,因为.getconsumineGenumerable()
会一直阻塞,直到BC中有另一个项目。我想我可以用任务来完成,只需在同一批中启动4或5个任务,但是:
TryTake
,这样如果没有什么东西可以拿,它们就不会阻塞,因为我不知道我是否总能达到BC中作为任务批处理的项目数,例如,只有一个任务我留在BC和一批4个任务中)我想我会按照您的要求来做,为什么不创建一个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){
...
}
}
获取序列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;
...
}
}
您的生产者代码段缺少一个paren,它也没有任何意义。有几件事:(1)使用
CancelationToken
而不是布尔标志来执行取消操作。(2)除非取消操作一直持续,否则您将何时处理一系列结果?(3)这些结果是什么?(4)您需要让制作人确定与enqueue上的ScannedPage
关联的序列号,或者ScannedPage
实例必须有序列号。您好,Alex。回应您提到的内容。1)谢谢!2)我有什么选择,这样它就不会永远继续,而是在每次检测到BC不是空的时候,以n个元素为一批处理项目?3) 我按顺序接收一些扫描的页面,在并行部分,我对每个图像执行独立的处理(读取条形码、旋转条形码等等)。在这部分之后,我需要按照图像的顺序保存图像(因此需要FIFO),因为保存图像的路径可能取决于之前的图像。4) 每个项目都有序列号。感谢您的快速回答。关于你的回答,我有几个问题:1)这能保证我的FIFO顺序吗(我的意思是,它会按照foreach的顺序排列)?2) 在我添加到队列后,我正在更改项目(在处理部分),这不会有问题吗?因为您正在并行化循环,因为顺序不保证,所以您需要在对象上使用一些标记来指示序列。我将编辑另一种线程安全方式,假设ScannedPage有一个SeqNbr
属性,您在前面设置了该属性来指示它的顺序。为什么一个包比一个BlockingCollection(队列)更好?除了BC之外,它对生产者/消费者没有什么不同,我只需要一个简单的线程安全列表。如果你愿意,它将与BC一起工作。嗨,谢谢!!!!!,我会的。现在是问题的另一部分。我有什么选择,使它不会永远继续(在Parallel.ForEach中,或在没有元素时阻塞),而是在每次检测到BC不是空的时候,以n个元素为一批处理项目?在BC中是否有某种Peek()方法会一直阻塞,直到其中有一个元素?
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;
...
}
}