У меня есть следующий код, вызывающий утечку памяти. При использовании await
внутри двух вложенных IAsyncEnumerables
.
Fun c work
- это функция, которая принимает входные данные из входного канала и возвращает IAsyncEnumerable
, который повторяется по помещению в выходной канал.
Если я использую одну из двух строк, помеченных // Works!
, код работает, как ожидалось, без создания огромной кучи.
Просмотр профилировщика памяти без утечки памяти , все выглядит нормально, объект DTO (TOut
) result
, возвращаемый перечислением «работа», ссылается только на выходной канал, как и ожидалось, с максимальным значением 10 в качестве ограниченного.
При запуске кода с утечкой памяти каждый объект DTO из work
IAsyncEnumerable
хранится в памяти со ссылкой в пути root, на функцию work
и ссылкой на некоторую stati c переменную Dictionary<Int32,Task> [Statis variable Task.s_currentActiveTasks]
как задачи, сгенерированные IAsyncEnumerator, никогда не завершаются и не удаляются.
public static ChannelReader<TOut> Pipe<TIn, TOut>(this ChannelReader<TIn> input, Func<TIn, IAsyncEnumerable<TOut>> work, CancellationToken cancellationToken = default)
{
var output = Channel.CreateBounded<TOut>(10);
_ = Task.Run(async () =>
{
try
{
await foreach (var item in input.ReadAllAsync(cancellationToken).ConfigureAwait(false))
{
await foreach (var result in work(item))
{
// Memory leak!
await output.Writer.WriteAsync(result, cancellationToken).ConfigureAwait(false);
// Works!
//while (!output.Writer.TryWrite(result))
// Thread.Sleep(1);
// Works!
//output.Writer.WriteAsync(result, cancellationToken).AsTask().Wait();
}
}
output.Writer.Complete();
}
catch (Exception ex)
{
output.Writer.Complete(ex);
}
});
return output;
}
Может ли кто-нибудь пролить некоторое представление об этой проблеме?
Если кто-то не может что-то заметить я пропустил серьезную ошибку, я постараюсь опубликовать минимально воспроизводимый пример. У меня еще нет большого опыта работы с IAsyncEnumerable.