У меня есть приложение .NET(С#), которое широко использует async/await. Я чувствую, что у меня голова вокруг async/await, но я пытаюсь использовать библиотеку (RestSharp), которая имеет более старую (или, может быть, я просто говорю другую) модель программирования, которая использует обратные вызовы для асинхронных операций.
Класс RestSharp RestClient
имеет метод ExecuteAsync, который принимает параметр обратного вызова, и я хотел иметь возможность помещать оболочку вокруг того, что позволило бы мне await
выполнить всю операцию. Метод ExecuteAsync
выглядит примерно так:
public RestRequestAsyncHandle ExecuteAsync(IRestRequest request, Action<IRestResponse> callback);
Я думал, что все это прекрасно работает. Я использовал TaskCompletionSource
, чтобы обернуть вызов ExecuteAsync
в то, что я мог ждать, следующим образом:
public static async Task<T> ExecuteRequestAsync<T>(RestRequest request, CancellationToken cancellationToken) where T : new()
{
var response = await ExecuteTaskAsync(request, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(response.Content);
}
private static async Task<IRestResponse> ExecuteTaskAsync(RestRequest request, CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<IRestResponse>();
var asyncHandle = _restClient.ExecuteAsync(request, r =>
{
taskCompletionSource.SetResult(r);
});
cancellationToken.Register(() => asyncHandle.Abort());
return await taskCompletionSource.Task;
}
Это отлично работает для большинства моих приложений.
Однако у меня есть одна часть приложения, которая выполняет сотни вызовов моего ExecuteRequestAsync
как часть одной операции, и эта операция показывает диалог прогресса с кнопкой отмены. Вы увидите, что в приведенном выше коде я передаю CancellationToken
в ExecuteRequestAsync
; для этой долговременной операции токен связан с CancellationTokenSource
"принадлежностью" к диалогу, метод Cancel
вызывается, если использование нажимает кнопку отмены. Пока все хорошо (кнопка отмены работает).
Моя проблема заключается в том, что во время работы приложения работает всплывающее приложение, в то время как у него заканчивается память до завершения операции.
Я запустил профайлер памяти и обнаружил, что у меня есть много объектов RestResponse
, все еще находящихся в памяти, даже после сбора мусора. (Они, в свою очередь, имеют огромное количество данных, потому что я отправляю файлы с несколькими мегабайтами через провод).
Согласно профилировщику, те объекты RestResponse
сохраняются в живых, потому что они ссылаются на TaskCompletionSource
(через Task
), который, в свою очередь, сохраняется в живых, поскольку он ссылается на CancellationTokenSource
через список зарегистрированных обратных вызовов.
Из всего этого я понимаю, что регистрация обратного вызова отмены для каждого запроса означает, что весь график объектов, связанных со всеми этими запросами, будет работать до завершения всей операции. Неудивительно, что у него заканчивается память: -)
Итак, я думаю, что мой вопрос не столько "зачем он течет", сколько "как его остановить". Я не могу не зарегистрировать обратный вызов, так что я могу сделать?