.NET: Должен ли я поддерживать ссылку на WebClient во время загрузки асинхронно?
Я использую следующий метод в фрагменте кода:
private void DownloadData(Uri uri)
{
WebClient webClient = new WebClient();
DownloadDataCompletedEventHandler eh = null;
eh = delegate(object sender, DownloadDataCompletedEventArgs e)
{
webClient.DownloadDataCompleted -= eh;
((IDisposable) webClient).Dispose();
OnDataDownloaded();
};
webClient.DownloadDataCompleted += eh;
webClient.DownloadDataAsync(uri);
}
Теперь я беспокоюсь, что ошибка с жестким воспроизведением может быть вызвана тем, что экземпляр WebClient
является сборкой мусора перед вызовом DownloadDataCompleted
: после выхода из моего метода DownloadData()
нет очевидных ссылок на WebClient
, чтобы это могло случиться.
Итак, мой вопрос: может ли это реально произойти? Я не могу воспроизвести проблему, поэтому могут произойти некоторые внутренние вещи, которые не позволяют объекту WebClient
быть собранным в мусор (например, объект может регистрироваться в глобальном объекте где-то в ожидании ответа).
Код работает на .NET 2.0, если это имеет значение.
Ответы
Ответ 1
Нет, ваш объект не будет GC-ed до завершения обратного вызова. Согласно Разве сборщик мусора уничтожает временно неопубликованные объекты во время асинхронных вызовов в .NET?, " асинхронный API сохраняет ссылку на ваш запрос (в пуле потоков где выполняются операции асинхронного ввода-вывода), и поэтому он не будет собирать мусор, пока он не завершится."
Но ваш код также делает то, что ему не нужно: вам не нужно отсоединять обработчик событий и не нужно вызывать Dispose на веб-клиенте. (Dispose() фактически не реализован WebClient - вы можете увидеть это в исходном источнике .NET Framework в http://referencesource.microsoft.com/netframework.aspx).
Поэтому вам действительно не нужно ссылаться на экземпляр webclient в своем обратном вызове. Другими словами, следующий код будет работать так же хорошо, и избегать любых потенциальных проблем (обсужденных выше) ссылок на внешние локальные переменные изнутри делегата.
private void DownloadData(Uri uri)
{
WebClient webClient = new WebClient();
DownloadDataCompletedEventHandler eh = null;
eh = delegate(object sender, DownloadDataCompletedEventArgs e)
{
OnDataDownloaded();
};
webClient.DownloadDataCompleted += eh;
webClient.DownloadDataAsync(uri);
}
В любом случае, вы, вероятно, захотите посмотреть в другом месте на источник вашей ошибки. Одно место, которое я бы посмотрел, - это результаты HTTP-вызовов - у вас может быть нехватка памяти, может быть запущена ошибка сервера и т.д. Вы можете посмотреть на e.Error, чтобы увидеть, действительно ли звонки работают.
Ответ 2
Я точно не знаю, может ли WebClient
быть собранным или нет, если выполняется асинхронная операция, потому что могут быть внутренние ссылки, но вопрос большой: имеет ли значение?
До тех пор, пока достаточно WebClient
останется "живым" для обслуживания запроса и вызовите обработчик, имеет ли значение, действительно ли главный объект WebClient
собирает мусор?
Документация WebClient
не упоминает о необходимости удерживать ссылку (в отличие от System.Threading.Timer
docs, например) поэтому я считаю разумным предположить, что это нормально.
В этом конкретном случае у вашего делегата есть ссылка на WebClient
, поэтому, если ссылается на сам делегат, WebClient
не может быть. Моя образованная догадка заключается в том, что какая-то часть системы где-то нуждается в обратном вызове, чтобы знать, что делать, когда прибывает сетевой трафик, и что обратный вызов в конечном итоге (косвенно) приведет к вашему делегату, так что вы в порядке.
Ответ 3
Вы можете попробовать отладить приложение с помощью инструментов отладки для Windows - он позволяет вам видеть, что именно хранит ссылки на определенный объект (с соответствующим подключаемым модулем -в). Очень полезно для таких случаев.
Я не знаю ответа на ваш вопрос. Одна из возможностей заключается в том, что в течение всего срока действия WebClient делает себя одним из "корневых" объектов, которые никогда не собираются с мусором (приложение .NET обычно имеет от 5 до 10 таких объектов, которые являются корнями нескольких используемых опорных деревьев по заявке). Это чистая спекуляция.
Ответ 4
Размешение монеты... если вы где-то сохранили ссылку на WebClient
, просто чтобы увидеть, не имеет ли это значение... это вообще устраняет проблему? Может быть, проще проверить это так и быть уверенным, чем угадать, что кажется логичным.
Ответ 5
При создании анонимного метода со ссылкой на переменную области видимости (webClient в вашем случае) и создайте ее собственную переменную со ссылкой на этот объект. Так как jon угадывает, что ваш делегат будет ссылаться на webClient и до того, как делегат отменит его сам, webClient не может быть собранным мусором.
Однако я обычно предлагаю не использовать ссылку webClient в методе делегата, а передавать отправителя во внутреннюю переменную. использование переменных извне анонимным методам может привести к некоторым очень странным ошибкам.