根据此处描述的文章MS Docs,我正在使用BlockingCollection
尝试一种非常简单的Producer Consumer方法,以了解多线程。
我的生产者是一个单独的任务,它从XML文件中读取(有大约4000个节点)并将XElement
个节点推送到阻塞集合。
我的消费者将有多个线程从阻止集合中读取并根据XElement
将文件上传到站点。
这里的问题是每次尝试运行程序时程序都会意外关闭。它击中了制作人Task.Run
但在此之后停止。我无法理解原因。难道我做错了什么?它甚至没有击中catch
区块。
代码如下:
BlockingCollection<XElement> collection = new BlockingCollection<XElement>(100);
string metadataFilePath = exportLocation + listTitle + "\\Metadata\\" + exportJobId + ".xml";
//create the producer
Task.Run(() =>
{
//Process only the files that have not been uploaded
XDocument xmlFile = XDocument.Load(metadataFilePath);
var query = from c in xmlFile.Elements("Items").Elements("Item")
where c.Attribute("IsUploaded").Value == "No"
select c;
foreach (var item in query)
{
collection.Add(item);
}
collection.CompleteAdding();
});
//process consumer
Parallel.ForEach(collection, (new System.Threading.Tasks.ParallelOptions { MaxDegreeOfParallelism = 2 }), (metadata) => {
ProcessItems();
});
答案 0 :(得分:2)
假设您正在尝试运行控制台应用程序,我可以考虑以下问题:
您可以看到更多示例here。 尝试在示例代码中注意以下事项:
在第一个帖子中调用方法 CompleteAdding(),表示该集合不再接受制作人添加的任何项目。一旦添加了所有项目,生产者线程就会调用它。在将一个集合标记为完成以进行添加之后,不允许添加到集合中,并且当集合为空时,尝试从集合中删除将不会等待。 / p>
第二个示例中的消费者线程使用 TryTake(out result)。 消费者线程启动并尝试取出值。即使生产者线程没有添加任何项目,它也将继续等待,因为收集尚未通过生产者线程调用CompleteAdding()标记为IsAddingCompleted。当集合被标记为IsAddingCompleted且集合为空时,消费者线程将从TryTake获得错误的返回值,即集合的IsCompleted属性变为true,允许消费者线程完成。
4.拨打 Console.ReadLine(),这样后台线程的任务都不会在没有完成的情况下终止。
希望这有帮助。
答案 1 :(得分:2)
answer by Nish26对于问题中的问题是正确的。
我建议用Microsoft TPL Dataflow解决你的生产者/消费者问题:
using System.Threading.Tasks.Dataflow;
var parallelBoundedOptions = new ExecutionDataflowBlockOptions
{
BoundedCapacity = 100,
MaxDegreeOfParallelism = 2,
};
var uploadItemBlock = new ActionBlock<XElement>(
item => ProcessItem(item),
parallelBoundedOptions
);
string metadataFilePath = exportLocation + listTitle + "\\Metadata\\" + exportJobId + ".xml";
XDocument xmlFile = XDocument.Load(metadataFilePath);
var query = from c in xmlFile.Elements("Items").Elements("Item")
where c.Attribute("IsUploaded").Value == "No"
select c;
foreach (var item in query)
{
uploadItemBlock.SendAsync(item).Wait();
}
uploadItemBlock.Complete();
uploadItemBlock.Completion.Wait();
Dataflow可以更轻松地专注于制作和使用这些项目,而不是如何将它们从生产者传递给消费者。
问题中的实际问题是Parallel.Foreach
正在使用BlockingCollection<T>.IEnumerable<T>.GetEnumerator
而不是BlockingCollection<T>.GetConsumingEnumerable
,如下所示:
static void Main()
{
var collection = new BlockingCollection<int>(100);
Task.Run(()=>
{
foreach (var element in Enumerable.Range(0, 100_000))
{
collection.Add(element);
}
collection.CompleteAdding();
});
Parallel.ForEach(
collection,
new ParallelOptions { MaxDegreeOfParallelism = 2},
i => Console.WriteLine(i));
Console.WriteLine("Done");
}
立即打印“完成”
static void Main()
{
var collection = new BlockingCollection<int>(100);
Task.Run(()=>
{
foreach (var element in Enumerable.Range(0, 100_000))
{
collection.Add(element);
}
collection.CompleteAdding();
});
Parallel.ForEach(
collection.GetConsumingEnumerable(),
new ParallelOptions { MaxDegreeOfParallelism = 2},
i => Console.WriteLine(i));
Console.WriteLine("Done");
}
打印所有数字