【发布时间】:2022-01-13 18:16:18
【问题描述】:
参见以下示例:
public static void ForgottenTask()
{
Action<object> action = (object obj) =>
{
Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj, Thread.CurrentThread.ManagedThreadId);
};
new Task(action, "alpha").ContinueWith(action);
}
static void Main(string[] args)
{
for (int i = 0; i < 1000000; i++)
ForgottenTask();
GC.Collect();
GC.Collect();
Debugger.Break();
}
显然没有执行任何操作,这是意料之中的。奇怪的是,当我在 Debugger.Break 期间通过菜单 -> 调试 -> Windows > 任务/并行堆栈检查任务时(在 Visual Studio 2022 中;我不知道有什么更简单的方法),我在 '计划的状态。我不确定它是调试限制还是调度程序的某种限制。那么我的第一个问题是,为什么是 10 000?
无论如何,这些任务不会被垃圾收集,这可能是预期的,因为它们在 TaskScheduler 中有引用。但我的问题是他们会发生什么?他们会永远挂在那里(听起来像内存泄漏)吗?或者它们会以某种方式被重复使用/移除?如果是这样,何时以及如何?
我在示例中使用了 .NET 6 和 VS 2022(如果相关)
【问题讨论】:
-
顺便说一句,您应该尽量避免使用
Task构造函数。请首选Task.Run或更多advanced scenariosTaskFactory.StartNew。 -
@PeterCsala 我认为
Task.Run和Task.Factory.StartNew都会消除内存泄漏,因为这些任务实际上会被执行。在这种情况下,任务永远不会执行,这就是它无法被清理的原因。 -
@PeterCsala:我读了前两个,但仍然不知道为什么。第三个似乎只是毫无疑问的答案(我觉得有点愚蠢,但我看不到那里的问题)。代码只是示例,我通常不会以这种方式创建任务。
-
@DavidL:我想这是真的,但这不是我原来的情况。我只是想提供一个完整的例子,它足够小而且容易。我最初的问题发生在 DataFlow 库中。 DataflowBlock 上有这个 Completion 属性link,它似乎是基于 TaskCompletionSource 的任务。当您不在块上调用完成时,此任务会泄漏。但我认为根本问题是一样的,我的例子似乎更容易展示。
标签: c# .net asynchronous task taskscheduler