Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 没有对任务的引用是否会导致内存泄漏?_C#_.net_Memory Leaks_Task Parallel Library - Fatal编程技术网

C# 没有对任务的引用是否会导致内存泄漏?

C# 没有对任务的引用是否会导致内存泄漏?,c#,.net,memory-leaks,task-parallel-library,C#,.net,Memory Leaks,Task Parallel Library,考虑以下代码段: public void Do() { .... Task.Delay(5000).ContinueWith(t => DoSomething()); .... } 假设Do方法在Delay任务完成之前完成执行,并且DoSomething不可取消 没有对ContinueWith方法返回的任务进行引用的事实是否会导致某种内存泄漏 任务将在线程池线程上执行,一旦任务完成,线程将返回到线程池供其他任务使用 线程池线程在空闲一段时间后(我认为默认情况下大约45秒

考虑以下代码段:

public void Do()
{
   ....
   Task.Delay(5000).ContinueWith(t => DoSomething());
   ....
}
假设
Do
方法在
Delay
任务完成之前完成执行,并且
DoSomething
不可取消


没有对
ContinueWith
方法返回的
任务
进行引用的事实是否会导致某种内存泄漏

任务将在线程池线程上执行,一旦任务完成,线程将返回到线程池供其他任务使用

线程池线程在空闲一段时间后(我认为默认情况下大约45秒)被回收

因此,线程池持有对它的引用这一事实将防止它被垃圾收集

我想唯一需要注意的是,主应用程序线程必须运行。例如,如果在控制台应用程序中运行上述代码,则执行将在任务之前完成,因此任务将永远不会运行

简而言之,否-该代码不会导致内存泄漏。

您可以通过创建未收集的新任务来泄漏(托管)内存吗?
是。

保留对它的引用(或不保留引用)对收集它有任何影响吗?
通常*不是。

有两种类型的任务:承诺(异步)任务和委托(同步)任务

  • 承诺任务(如
    Task.Delay
    )通常不会被收集,因为某些东西会保留对它的引用,以便它可以在需要时更改其状态(在
    Task.Delay
    中,当延迟结束时,需要完成
    任务的是内部
    计时器
  • 委托任务()由运行它们的线程(和
    TaskScheduler
    )引用
所以,如果你继续创建任务,无论你是否持有引用,你都可能有漏洞

在您的特定情况下,在前5秒钟,内部
计时器
引用Promise
任务
,该任务反过来引用委托
任务
(该任务尚未计划,并且没有与其关联的线程)。5秒后,
计时器
完成
任务。延迟
任务,该任务依次安排
DoSomething
继续,因此线程将引用它

如果
DoSomething
从未完成,则内存泄漏(非常小),如果确实完成,则不会


*您可以创建仅由您引用的任务(两种类型),当您不再引用它们时,它们可以由
GC
收集。因此,尽管如此:

static void Main()
{
    while (true)
    {
        Task.Delay(int.MaxValue);
    }
}
将在几秒钟内导致
OutOfMemoryException
,这可能会永远运行:

static void Main()
{
    while (true)
    {
        new Task(() => Thread.Sleep(int.MaxValue)); // No thread as the task isn't started.
        Task.Delay(-1); // No Timer as the delay is infinite.
    }
}

如果你的代码只是
Task.Delay(5000),你可以说同样的话-“未维护对返回任务的引用”,我很确定这不会泄漏。我认为即使您愿意,也不可能在托管上下文中泄漏内存。当引用类型的实例没有引用时,GC最终会来收集它。虽然不是泄漏,但您的任务将是“未观察到的”,如果抛出任何异常,未观察到的任务将导致混乱。它只能由任务终结器检测到,任务终结器在垃圾收集过程中以无法控制的时间运行。所以我还是建议重写你的代码。谢谢,很好的解释。那么不来自线程池的长时间运行的任务呢?您是指长时间运行标志吗?如果是这样,这只会向调度程序提供一个提示,提示它可能希望在非线程池线程上执行任务。即使它确实在非线程池线程上执行,它仍然是一个托管线程,并且在执行完成后将被清除。此外,Stephen Cleary在本文中指出,在大多数情况下,您可能不需要LongRunning标志,因为调度器能够在0.5秒内调整到任何长时间运行的任务:
task.Delay(-1)
也会导致内存泄漏,这与
task.Delay(int.MaxValue)
(至少在.NET 4.6上是这样)@marchewek你认为这会泄露什么?哪一类的实例?