当对象超出范围时,停止等待的任务的正确方法是什么



我的应用程序有两个不同的状态,每个状态都有一组独立的资源,但一次只能有一个处于活动状态。

状态由一个类表示,该类保存对各种服务的托管引用。为了简单起见,我们假设一下:

class FirstApplicationState {
ServiceA FooService { get; }
ServiceB BarService { get; }
}
// Some 'global' object holds reference to State
Application.State = new FirstApplicationState();

ServiceA类在执行期间启动延迟执行任务。

class ServiceA {
CancellationTokenSource tsc;

private async Task TriggerDelayedActionAsync() {
try {
tsc = new CancellationTokenSource();
await Task.Delay(TimeSpan.FromMinutes(1),tsc.Token);
}
catch(TaskCancelledException) {
Log.Info("Task cancelled");
return;
}
finally { /*dispose tsc*/ }
await RunVeryLongTaskAsync();
}
}

如果FirstApplicationState不再是应用程序的活动状态,则该任务不应再执行。如果状态将超出范围,并且在等待Task.Delay()方法时,通过调用Application.State = null将整个层次结构标记为要收集,则ServiceA的实例将不会标记为GC收集,等待将最终执行,调用RunVeryLongTaskAsync方法,这将加载一些资源并可能引发异常。

为了避免这种情况,自然的解决方案是向服务引入一些Deinitialize()方法,并运行一个去初始化过程,该过程将在应用程序允许标记GC收集的状态之前正确触发所有取消令牌,尽管这似乎需要跟踪所有潜在任务的取消令牌。

我想知道是否有其他机制,一旦执行任务的对象超出范围,就可以取消所有任务?虽然不依赖它来进行正确的状态清理,但也许我可以检查整个对象层次结构上是否有任何任务仍在运行以进行调试?我也在考虑使用双源取消令牌,这将允许我进行一些更高级别的取消,如微软文档中所述,但我不确定这种做法有多普遍,因为我以前从未遇到过类似的解决方案。

如果FirstApplicationState不再是应用程序的活动状态,则任务不应再执行。

检查这一点的最简单方法是让方法本身检查它是否仍应运行。如果你只需要在某个特定点检查一次,我建议你这样做。

ServiceA的实例不会标记为GC收集。。。也许我可以检查整个对象层次结构上是否有任何任务仍在运行以进行调试?

实际上;层次结构";。问题是,任务存在于以相反的方式链接的内存中。从TriggerDelayedActionAsync返回的任务根本没有引用从Task.Delay返回的任务。相反的实际上是正确的。计时器充当GC根,引用从Task.Delay返回的任务,该任务具有从TriggerDelayedActionAsync返回的TriggerDelayedActionAsync的延续。

为了避免这种情况,自然的解决方案是。。。触发所有取消令牌。。。尽管似乎需要跟踪所有潜在任务的取消令牌。

是。如果你只需要检查一次(例如,在长任务开始之前),那么你可以在那时只检查一次。但是,如果您需要随时取消,那么CancellationToken将是一个合适的解决方案。正如您所指出的,链接的取消令牌可能也是必要的。

没有任何内置功能;如果你需要支持的话,你必须在任何地方添加这些取消令牌。

如果您想列出用于调试的任务,可以使用TaskScheduler.GetScheduledTasks(您可以从Scheduler.ThreadPool中获取默认调度程序)

或者实现您自己的TaskScheduler并将其用于TaskFactory

最新更新