C#async,在完成一个任务而不访问代码时停止所有任务

时间:2013-10-26 21:09:28

标签: c# multithreading asynchronous

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

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

我在.NET中使用async / await模型和CancellationToken来取消任务。

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;
            }
        }

某些provider.DoWork()方法可能非常慢,并且要停止DoWork方法执行的线程,我必须在DoWork方法中检查token.IsCancellationRequested,但我不能,因为这些方法是从其他人写的,例如。 在这种情况下,对DoWork外部的token.IsCancellationRequested的检查并不重要。

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

期待听到您对此的看法。

2 个答案:

答案 0 :(得分:2)

在一个进程中突然终止一个线程有很多问题 - 基本上,这应该只是作为最后的手段,当进程如此恶心以至于你无论如何都要杀死整个事件。除此之外的任何事情,如果你干涉,你将面临重大问题的严重风险。所以:那将留下优雅的选择性退出。你提到你已经在使用取消令牌;真的,这就是你能做的一切(至少安全)。由不同的实现来检查取消和退出。如果您不能依赖于定期检查取消的任务:您有点卡住了。

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

答案 1 :(得分:0)

有几种不同的方法可以阻止任意代码,但这些方法都不理想。

首先,您可以在线程级别上进行此操作。换句话说,为每个提供者提供一个单独的线程,并在您希望它停止时调用Thread.Abort。不幸的是,这种方法存在无数的问题:两个更为人熟知的问题是死锁的可能性,并进入一个奇怪的状态,其中一个类型不能再在你的AppDomain中实例化。出于这个原因,Thread.Abort被认为是“邪恶的”和明确的代码气味。

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

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

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

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