计划但从未执行的任务会发生什么?

What happens with scheduled but never executed tasks?

参见以下示例:

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 > Tasks/Parallel 堆栈检查任务时(在 Visual Studio 2022 年;我不知道更简单方式),我看到其中 10 000 个处于 'Scheduled' 状态。我不确定它是调试限制还是调度程序的某种限制。那么我的第一个问题是,为什么是 10 000?

无论如何,这些任务不会被垃圾收集,这可能是意料之中的,因为它们在 TaskScheduler 中有引用。但我的问题是他们会发生什么?他们会永远挂在那里吗(听起来像内存泄漏)?或者他们会以某种方式reused/removed?如果是这样,何时以及如何?

我在示例中使用了 .NET 6 和 VS 2022(如果相关的话)

我不太了解使用 Visual Studio 的调试功能的含义,但是如果您 运行 您的程序没有附加调试器(使用 Ctrl+F5),任务是由垃圾收集器正确回收。 ForgottenTask 方法内部创建的任务没有启动,所以它们没有被调度,并且由于您没有持有任何对任务的显式引用,所以没有什么可以阻止垃圾收集器回收它们。这是此行为的最小演示:

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        var weakReference = ForgottenTask();
        Console.WriteLine($"Before GC.Collect, IsAlive: {weakReference.IsAlive}");
        GC.Collect();
        Console.WriteLine($"After GC.Collect, IsAlive: {weakReference.IsAlive}");
    }

    static WeakReference ForgottenTask()
    {
        var task = new Task(() => { }).ContinueWith(_ => { });
        return new WeakReference(task);
    }
}

输出:

Before GC.Collect, IsAlive: True
After GC.Collect, IsAlive: False

Try it on Fiddle.