当对象超出范围时停止等待任务的正确方法是什么?
What is the proper way to stop awaited tasks when object goes out of scope?
我的应用程序有两个不同的状态,每个状态都有独立的一组资源,但一次只能激活其中一个。
状态由 class 表示,其中包含对各种服务的托管引用。为简单起见,我们假设:
class FirstApplicationState {
ServiceA FooService { get; }
ServiceB BarService { get; }
}
// Some 'global' object holds reference to State
Application.State = new FirstApplicationState();
执行期间ServiceA
class启动延迟执行任务。
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
不再是应用程序的活动状态,则任务不应再执行。如果状态将超出范围并且整个层次结构将被标记为通过调用 Application.State = null
来收集,同时正在等待 Task.Delay()
方法,则 ServiceA
的实例将不会被标记对于 GC 收集,await 最终将执行,调用 RunVeryLongTaskAsync
方法,这将加载一些资源并可能抛出异常。
为避免这种情况,自然的解决方案是向服务引入一些 Deinitialize()
方法和 运行 一个取消初始化过程,该过程将在应用程序允许标记状态之前正确触发所有取消令牌GC 收集,尽管似乎需要为所有潜在任务跟踪取消令牌。
我想知道是否有其他机制可以让我在执行任务的对象超出范围时取消所有任务?虽然不依赖它来进行适当的状态清理,但也许我可以检查整个对象层次结构上的任何任务是否仍在 运行ning 以进行调试?我也在考虑使用双源取消令牌,这将允许我进行一些更高级别的取消,如 in Microsoft's docs 所述,但我不确定这种做法有多常见,因为我之前没有遇到过类似的解决方案.
如果您想列出任务以进行调试,您可以使用
TaskScheduler.GetScheduledTasks(您可以从 Scheduler.ThreadPool
获取默认调度程序)
或implement your own TaskScheduler并在TaskFactory
中使用它
The task should no longer execute if the FirstApplicationState is no longer the active state of the application.
检查这一点的最简单方法是让方法本身检查它是否仍然应该 运行。如果您只需要在特定点检查一次,那是我的建议。
the instance of ServiceA won't be marked for GC collection... maybe I can check if any tasks on entire hierarchy of objects are still running for debugging purposes?
实际上没有“等级”。问题是内存中的任务以您需要的 相反 方式链接。从 TriggerDelayedActionAsync
返回的任务根本不引用从 Task.Delay
返回的任务。 相反的其实是真的。计时器充当 GC 根,引用从 Task.Delay
返回的任务,它有一个延续 TriggerDelayedActionAsync
,它引用从 TriggerDelayedActionAsync
.
返回的任务
To avoid it, the natural solution would be to ... trigger all cancellation tokens... although it seems like requiring tracking cancellation tokens for all potential tasks.
是的。如果你只需要检查一次(例如,在长任务开始之前),那么你可以在那个时候进行一次检查。但是如果你需要可以随时取消,那么一个CancellationToken
would be an appropriate solution。如您所述,可能还需要链接的取消令牌。
没有内置的东西;如果需要支持,则必须在任何地方添加这些取消标记。
我的应用程序有两个不同的状态,每个状态都有独立的一组资源,但一次只能激活其中一个。
状态由 class 表示,其中包含对各种服务的托管引用。为简单起见,我们假设:
class FirstApplicationState {
ServiceA FooService { get; }
ServiceB BarService { get; }
}
// Some 'global' object holds reference to State
Application.State = new FirstApplicationState();
执行期间ServiceA
class启动延迟执行任务。
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
不再是应用程序的活动状态,则任务不应再执行。如果状态将超出范围并且整个层次结构将被标记为通过调用 Application.State = null
来收集,同时正在等待 Task.Delay()
方法,则 ServiceA
的实例将不会被标记对于 GC 收集,await 最终将执行,调用 RunVeryLongTaskAsync
方法,这将加载一些资源并可能抛出异常。
为避免这种情况,自然的解决方案是向服务引入一些 Deinitialize()
方法和 运行 一个取消初始化过程,该过程将在应用程序允许标记状态之前正确触发所有取消令牌GC 收集,尽管似乎需要为所有潜在任务跟踪取消令牌。
我想知道是否有其他机制可以让我在执行任务的对象超出范围时取消所有任务?虽然不依赖它来进行适当的状态清理,但也许我可以检查整个对象层次结构上的任何任务是否仍在 运行ning 以进行调试?我也在考虑使用双源取消令牌,这将允许我进行一些更高级别的取消,如 in Microsoft's docs 所述,但我不确定这种做法有多常见,因为我之前没有遇到过类似的解决方案.
如果您想列出任务以进行调试,您可以使用
TaskScheduler.GetScheduledTasks(您可以从 Scheduler.ThreadPool
获取默认调度程序)
或implement your own TaskScheduler并在TaskFactory
The task should no longer execute if the FirstApplicationState is no longer the active state of the application.
检查这一点的最简单方法是让方法本身检查它是否仍然应该 运行。如果您只需要在特定点检查一次,那是我的建议。
the instance of ServiceA won't be marked for GC collection... maybe I can check if any tasks on entire hierarchy of objects are still running for debugging purposes?
实际上没有“等级”。问题是内存中的任务以您需要的 相反 方式链接。从 TriggerDelayedActionAsync
返回的任务根本不引用从 Task.Delay
返回的任务。 相反的其实是真的。计时器充当 GC 根,引用从 Task.Delay
返回的任务,它有一个延续 TriggerDelayedActionAsync
,它引用从 TriggerDelayedActionAsync
.
To avoid it, the natural solution would be to ... trigger all cancellation tokens... although it seems like requiring tracking cancellation tokens for all potential tasks.
是的。如果你只需要检查一次(例如,在长任务开始之前),那么你可以在那个时候进行一次检查。但是如果你需要可以随时取消,那么一个CancellationToken
would be an appropriate solution。如您所述,可能还需要链接的取消令牌。
没有内置的东西;如果需要支持,则必须在任何地方添加这些取消标记。