C# 异步,当一个任务完成而无法访问代码时停止所有任务

本文关键字:任务 访问 代码 异步 一个 | 更新日期: 2023-09-27 18:37:02

我正在编写一个在 .NET 中使用提供程序模型模式的小应用程序。每个提供程序都有一个要实现的方法,以便在我的应用程序中插入。

我想在不同的线程中运行所有提供程序的特定工作(实现的方法),但是当一个任务完成时,所有其他任务都需要停止执行。

我正在使用带有取消令牌的 .NET 中的异步/等待模型来取消任务。

tasks.Add(Task.Run(() =>
                {
                        if (token.IsCancellationRequested)
                            token.ThrowIfCancellationRequested();
                        return provider.DoWork();
                }, token));
while (tasks.Count > 0)
        {
            var t = await Task.WhenAny(tasks);
            tasks.Remove(t);
            var result = await t;
            if (!string.IsNullOrEmpty(result))
            {
                tokenSource.Cancel();
                return result;
            }
        }

一些提供商。DoWork() 方法可能非常慢,要停止执行 DoWork 方法的线程,我必须检查令牌。IsCancelRequest在DoWork方法中请求,但我不能,因为这些方法是从其他人那里写的,例如。令牌的检查。在这种情况下,在DoWork之外请求的IsCancelRequest并不重要。

总而言之,当一个任务完成时,必须停止其他任务,但无法访问每个线程中运行的代码。

期待听到你对此的看法。

C# 异步,当一个任务完成而无法访问代码时停止所有任务

突然

终止进程中的线程有无数问题 - 本质上,只有当进程病态到无论如何都要杀死整个事情时,才应该作为最后的手段。除此之外的任何事情,如果你干涉,你就有可能造成重大问题。所以:这留下了优雅的选择性退出。您提到您已经在使用取消令牌;真的,这就是你能做的(至少是安全的)。由不同的实现来检查取消和退出。如果您不能依靠定期检查取消的任务:您有点卡住了。

基本上,对于您所描述的内容,没有简单的答案。

有几种不同的可能方法可以停止任意代码,但没有一种是理想的。

首先,您可以在线程级别处理此问题。换句话说,为每个提供程序提供一个单独的线程,并在您希望它停止时调用Thread.Abort。遗憾的是,这种方法存在无数问题:两个更广为人知的问题是死锁的可能性,以及进入一种奇怪的状态,即无法再在 AppDomain 中实例化某个类型。出于这个原因,Thread.Abort被认为是"邪恶的"和明确的代码气味。

再往上移动一个级别,您可以在 AppDomain 级别上解决此问题。在这种情况下,您可以为每个提供程序提供一个单独的应用程序域。此方法的优点是,您可以比线程更干净地关闭 AppDomain。不幸的是,有些事情仍然可能从裂缝中溜走:特别是,非托管资源可能会从 AppDomain 泄漏到您的进程中。

这为您提供了最可靠的解决方案:在流程级别上解决这个问题。也就是说,为每个提供程序提供一个单独的进程,并使用进程间通信来控制它。进程可以干净地关闭,因为操作系统负责清理。不幸的是,这种方法的开销最大,并且最难编程。

强大的托管解决方案必须至少支持最后一个。 例如,ASP.NET 支持所有三个:在某些情况下(例如,客户端断开连接)可以中止请求线程,可以回收 AppDomain,并且可以将其升级到回收整个工作进程的位置。但是,我建议您不要编写自己的带有升级策略的托管平台。

我建议的是 1) 向您的提供商提供CancellationToken并鼓励他们使用它,以及 2) 在您发出令牌信号后,让它们运行到完成。