Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在C中使用TPL的生产者-消费者模式中的异常处理#_C#_Multithreading - Fatal编程技术网

C# 在C中使用TPL的生产者-消费者模式中的异常处理#

C# 在C中使用TPL的生产者-消费者模式中的异常处理#,c#,multithreading,C#,Multithreading,我正在使用TPL支持在C#中实现多线程生产者-消费者模式。有了方便的BlockingCollection和Task类,这项工作很容易,直到我需要处理异常为止 众所周知,当BlockingCollection中没有可供消费的项目时,consumer Tasks对象将被阻止,为了防止这些任务无限期地等待,调用BlockingCollection.CompleteAdding()是至关重要的明确告诉所有人不再向BlockingCollection添加项目,并唤醒任何等待的任务 在我的例子中,在启动多个

我正在使用TPL支持在C#中实现多线程生产者-消费者模式。有了方便的BlockingCollection和Task类,这项工作很容易,直到我需要处理异常为止

众所周知,当BlockingCollection中没有可供消费的项目时,consumer Tasks对象将被阻止,为了防止这些任务无限期地等待,调用BlockingCollection.CompleteAdding()是至关重要的明确告诉所有人不再向BlockingCollection添加项目,并唤醒任何等待的任务

在我的例子中,在启动多个使用者和调用BlockingCollection.CompleteAdding()之间,可能会抛出一个异常。抛出异常时,执行将停止,这意味着永远不会调用BlockingCollection.CompleteAdding(),任务将永远等待

在我的例子中,我有一个大蛋糕,生产者-消费者只是其中的一块,而异常可以从蛋糕的其他部分抛出,这意味着生产者-消费者的蛋糕没有简单的方法来检测异常和处理清理


遇到这种情况有什么有用的模式吗?

我建议您使用
finally
块,该块调用
completedadding()
,以确保队列始终关闭,即使抛出异常也是如此。

对于最简单的情况,以下几点就足够了:

producer.ContinueWith(t => consumable.CompleteAdding(),
    TaskContinuationOptions.OnlyOnFaulted);
对于生产者和消费者长链的更复杂的情况,或者可以阻止生产者的上限BlockingCollections,这变得很棘手,因为任何未处理的异常都可能导致所有链接任务无限期地被阻止。对我有效的解决方案是使用BlockingCollection类的内置取消功能:

var cts = new CancellationTokenSource();
var consumable = new BlockingCollection<T>(10000);
//...
consumable.Add(item, cts.Token);
//...
consumable.GetConsumingEnumerable(cts.Token)

您可能需要共享一些代码。如果不在生产者线程上抛出异常,那么调用CompleteAdding就没有任何问题。若它被抛出到生产者线程上,那个么您的completedadding可以进入finally块。
producerTask.ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
intermediateTask.ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
consumerTask.ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
Task.WhenAll(producerTask, intermediateTask, consumerTask).Wait();
// This way the OperationCanceled exceptions are swallowed