Разве ссылка на задачу не вызывает утечек памяти?

Рассмотрим следующий фрагмент кода:

public void Do()
{
   ....
   Task.Delay(5000).ContinueWith(t => DoSomething());
   ....
}

Предположим, что Do метод завершает выполнение до Delay задача завершена и что DoSomething не подлежит отмене

Имеет ли тот факт, что ссылка на Task вернулся ContinueWith способ вызвать какую-то утечку памяти?

2 ответа

Решение

Задача будет выполнена в потоке пула потоков, после того как задача будет завершена, поток будет возвращен в пул для использования другой задачей.

Потоки потоков возвращаются после простоя в течение некоторого времени (я думаю, по умолчанию около 45 секунд).

Таким образом, тот факт, что пул потоков содержит ссылку на него, предотвратит сбор мусора.

Единственное предостережение, которое я предполагаю, заключается в том, что основной поток приложения должен быть запущен. Если вы запустили приведенный выше код в консольном приложении, например, выполнение завершилось бы до выполнения задачи, поэтому задача никогда не запустится.

Короче говоря, нет - этот код не вызовет утечку памяти.

Вы можете утечь (управляемую) память, создавая новые задачи, которые не собираются?
Да.

Влияет ли сохранение ссылки на него (или нет) на сбор информации?
Обычно * нет.

Существует 2 типа задач: задачи Promise (асинхронные) и задачи Delegate (синхронизация).

  • Обещание задач (например, Task.Delay) обычно не собираются, поскольку что-то хранит ссылку на него, поэтому оно может изменить свое состояние, когда это необходимо (в Task.Delay это внутренний Timer что должно завершить Task когда задержка заканчивается).
  • На задачи делегата ( как объяснил RagtimeWilly) ссылается поток (и TaskScheduler) вот и запускаю их.

Таким образом, если вы продолжаете создавать задачи, у вас может возникнуть утечка, независимо от того, держите вы ссылку или нет.

В вашем конкретном случае в течение первых 5 секунд внутренний Timer ссылается на обещание Task который в свою очередь ссылается на делегата Task (который еще не запланирован и с ним не связан поток). Через 5 секунд Timer завершает Task.Delay задача, которая в свою очередь планирует 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.
    }
}
Другие вопросы по тегам