Разве ссылка на задачу не вызывает утечек памяти?
Рассмотрим следующий фрагмент кода:
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.
}
}