Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.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# 在.NET中使用异常取消多个并行复杂操作的成本是多少?_C#_.net_Windows_Multithreading - Fatal编程技术网

C# 在.NET中使用异常取消多个并行复杂操作的成本是多少?

C# 在.NET中使用异常取消多个并行复杂操作的成本是多少?,c#,.net,windows,multithreading,C#,.net,Windows,Multithreading,TL;DR:取消最里面的通话可以吗?我希望我的代码既可维护又快速。但可维护性是最重要的 在我的第一种方法中,我尽可能避免基于异常的取消。这导致代码完全不可读,错误太多,行为几乎是随机的。我无法修复它,我只是使用基于异常的方法从一开始就重写了它。现在它工作了,我的工作也完成了,但我想了解更多,所以 问题如下。我有一个程序,可以同时下载数千个小文件 这些文件是分批组织的,一批包含大约一千个文件。当整个批处理完成后,我会更新一些元数据,以便应用程序知道哪些批处理已完成,它们的结构如何,哪些文件已下载(

TL;DR:取消最里面的通话可以吗?我希望我的代码既可维护又快速。但可维护性是最重要的

在我的第一种方法中,我尽可能避免基于异常的取消。这导致代码完全不可读,错误太多,行为几乎是随机的。我无法修复它,我只是使用基于异常的方法从一开始就重写了它。现在它工作了,我的工作也完成了,但我想了解更多,所以

问题如下。我有一个程序,可以同时下载数千个小文件

这些文件是分批组织的,一批包含大约一千个文件。当整个批处理完成后,我会更新一些元数据,以便应用程序知道哪些批处理已完成,它们的结构如何,哪些文件已下载(校验和),等等

当然,我可以点击(开始下载)任意多个批次,但是我有一个信号灯,它只允许
n
并发下载,所以没有什么不好的事情发生,所有内容都是以最快的方式下载的

现在我有一些边缘案例:应用程序在下载过程中突然关闭,某个特定批次被取消,或者-下载过程中出现网络错误

在每种边缘情况下,我都需要立即停止所有下载任务,写入所有计算出的校验和以及其他应用程序状态,这样用户就不需要重新下载文件

我引入了两个
CancellationToken
s,一个全局,在应用程序
OnExit
覆盖中取消,第二个本地,每个批,用于取消特定的批下载。然后使用
CancellationTokenSource.CreateLinkedTokenSource()
将它们组合在一起,以简化依赖代码

批量下载过程非常复杂和异步,假设它在
LoadAsync()
方法中。该方法调用许多其他方法,创建一个下载任务列表,重点是-每个依赖的异步方法都使用组合的令牌,并且可以在多个点中取消。当然
HttpClient
也会收到取消令牌

我观察到当cancel事件发生时,
HttpClient
抛出异常,
OpeationCanceledException
type

因此,在最终得到异常的循环中,我有适当的
块来处理它们。关键的一点是异常是通过几个方法调用传播的,这些方法调用将被最外层的方法捕获

现在它变得有趣了:当我使用VisualStudio调试运行代码时,取消过程会导致巨大的停顿,最大1个CPU核心负载,并持续几秒钟。这是因为VS存储每个异常的调用堆栈和其他元数据。让我们假设有数千个

当运行时没有调试-取消是即时的,效果是即时的。在不到1个GUI框架内取消了所有数千次下载。我单击“取消”的那一刻,一切都停止了,与我取消1个文件下载时完全相同

我的问题是-当异常通过代码传播时,到底会发生什么?这是取消多个复杂并行任务的最佳方法,还是有更快的方法来改变我的代码流

我在代码中使用了很多
token.throwifcancellationrequest()
。嗯-看起来微软在
HttpClient
中使用了它,因此生成的代码非常一致、整洁且可读<代码>最后
块保证我的信号量和其他可支配资源得到适当释放,为与取消和网络错误相关的错误留下很少的空间

但它是应该如何实现的,还是可以在不牺牲可读性和简单性的情况下进行优化


没有代码示例,因为它将是巨大的。简单的例子要么需要我花很长时间来编写(我现在还没有),要么太简单,无法观察异常通过代码传播时会发生什么。

OperationCanceledException
是在TPL中取消任务的默认方法。为什么不切换到并不传播取消异常?@VMAtm我读了,我读了,我读了,我读了,我找不到我当前版本的下载代码和TAP之间的差异。在我自己的异步方法中,我不能直接访问其任务对象来更改其状态,但我可以抛出
OperationCanceledException
。这是最直接的方法。结果几乎相同,任务以取消状态完成
Task.WhenAny()
Task.WhenAll()
相应地进行操作。顺便说一句,异常完全按照它应该的方式传递控件。它需要大量详细的代码来模拟这种行为。我也这么认为,但昨天我进行了调查,结果发现:1.>执行此操作的首选方法是使用
ThrowIfCancellationRequested
方法。2.当
操作取消异常
thrown@VMAtm我尽可能地通过取消安装请求使用
。它工作得很好,但在VS下调试有点不友好,因为它在异常方面滞后很多。嗯,取消本身是一个特殊的操作,所以我不经常测试它。我在不进行调试的情况下测试错误和取消处理,以查看其在重载情况下的性能。您可以调整异常设置,使其不会在特定类型的异常上中断,以便调试更容易。
OperationCanceledException
是在TPL中取消任务的默认方式。为什么不切换到并不传播取消异常?@VMAtm我读了,我读了,我读了,我读了,我找不到我当前版本的下载代码和TAP之间的差异。在我自己的异步方法中,我不能只访问它的任务