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

Posted

技术标签:

【中文标题】计划但从未执行的任务会发生啥?【英文标题】:What happens with scheduled but never executed tasks?计划但从未执行的任务会发生什么? 【发布时间】: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(如果相关)

【问题讨论】:

请阅读以下内容:1、2、3 顺便说一句,您应该尽量避免使用 Task 构造函数。请首选Task.Run 或更多advanced scenarios TaskFactory.StartNew @PeterCsala 我认为Task.RunTask.Factory.StartNew 都会消除内存泄漏,因为这些任务实际上会被执行。在这种情况下,任务永远不会执行,这就是它无法被清理的原因。 @PeterCsala:我读了前两个,但仍然不知道为什么。第三个似乎只是毫无疑问的答案(我觉得有点愚蠢,但我看不到那里的问题)。代码只是示例,我通常不会以这种方式创建任务。 @DavidL:我想这是真的,但这不是我原来的情况。我只是想提供一个完整的例子,它足够小而且容易。我最初的问题发生在 DataFlow 库中。 DataflowBlock 上有这个 Completion 属性link,它似乎是基于 TaskCompletionSource 的任务。当您不在块上调用完成时,此任务会泄漏。但我认为根本问题是一样的,我的例子似乎更容易展示。 【参考方案1】:

我不太了解使用 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.

【讨论】:

很抱歉反应这么慢。我目前很忙,我想仔细检查您的答案,如果证明正确,我会接受。我不会是第一次调试器背叛我,但它总是突然冒出来一个惊喜。 @robot40q 哈哈!我个人很少被调试器背叛,因为我很少使用它。每次面对如此复杂的错误时,我都讨厌我的生活,以至于我别无选择,只能使用调试器。 :-)

以上是关于计划但从未执行的任务会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

VB脚本手动运行完美,但无法通过计划任务管理器更新excel文件

windows2008定时计划任务未执行成功,报:因为用户未登陆到网络,因此未执行所有操作是啥原因导致?求助

任务计划程序无法正确执行批处理文件

windows任务计划程序是怎么回事,有啥用途,该怎么设置??

完成领导交代的任务叫啥优势

任务计划程序打开软件的原理