计划但从未执行的任务会发生什么?
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
参见以下示例:
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