Это не имеет большого значения для меня, поскольку это объект IDisposable, не должен ли GC также ссылаться на метод Dispose() объекта?
Dispose pattern, также известный как IDisposable, предоставляет два способа очистки неуправляемого объекта. Метод Dispose обеспечивает прямой и быстрый способ очистки ресурса. Метод finalize, который вызывается сборщиком мусора, является отказоустойчивым способом удостовериться, что неуправляемый ресурс очищается, если другой разработчик, использующий код, забывает вызвать метод Dispose.Это несколько похоже на то, что разработчики C++ забывают называть Delete в памяти, выделенной кучей, что приводит к утечке памяти.
По ссылочной ссылке:
«Хотя финализаторы эффективны в некоторых сценариях очистки, они имеют два существенных недостатка:
Финализатор вызывается, когда GC обнаруживает, что объект имеет права для коллекции. Это происходит в некоторый неопределенный промежуток времени после того, как ресурс больше не нужен. Задержка между тем, когда разработчик может или хочет освободить ресурс, и время, когда ресурс фактически выпущен финализатором, может быть неприемлемым в программах которые приобретают много скудных ресурсов (ресурсов, которые могут быть легко исчерпаны) или в случаях, когда ресурсы являются дорогостоящими для использования (например, большими неуправляемыми буферами памяти).
Когда CLR необходимо вызвать финализатор, он должен отложить сборку памяти объекта до следующего раунда сборки мусора (финализаторы выполняются между коллекциями). Это означает, что память объекта (и все объекты, это относится к) не будут выпущены в течение более длительного периода времени «.
Используя этот метод, сокеты никогда не переходит в состояние TIME_WAIT. То же самое, если Я просто закрыть приложение и вручную вызова Close() или Dispose()
Может кто-нибудь пролить некоторый свет и объяснить, будет ли это хорошая практика (который я полагаю, люди собираются, чтобы сказать, что это не так).
Причина, по которой для этого требуется время выключение происходит из-за того, что код lingers by default предоставляет приложению некоторое время для обработки любых сообщений в очереди. Согласно методу TcpClient.Close doc на MSDN:
«Метод Close указывает экземпляр как расположенный и запрашивает, чтобы связанный Socket закрывал TCP-соединение. Основываясь на свойстве LingerState, TCP-соединение может оставаться открытым в течение некоторого времени после Метод «Закрыть» вызывается, когда данные еще не отправлены. Уведомление не предоставляется, когда базовое соединение завершило закрытие.
Вызов этого метода в конечном итоге приведет к закрытию связанного сокета и также приведет к закрытию связанного с ним сетевого потока используется для отправки и получения данных, если они были созданы ».
Это значение тайм-аута может быть уменьшено или полностью устранено following code:
// Allow 1 second to process queued msgs before closing the socket.
LingerOption lingerOption = new LingerOption (true, 1);
tcpClient.LingerState = lingerOption;
tcpClient.Close();
// Close the socket right away without lingering.
LingerOption lingerOption = new LingerOption (true, 0);
tcpClient.LingerState = lingerOption;
tcpClient.Close();
Также интересно узнать, будет ли это хорошая практика, чтобы использовать этот метод для предотвращения TIME_WAIT состояний и, в конечном счете, является ли это ошибка, где-то (то есть, если все сокеты пройти через состояние TIME_WAIT?)
Что касается установки ссылки на объект TcpClient в нуль, рекомендуемый подход заключается в вызове метода Close , Когда для ссылки задано значение null, GC заканчивает вызов метода finalize. Метод finalize в конечном итоге вызывает метод Dispose для консолидации кода для очистки неуправляемого ресурса. Таким образом, он будет работать, чтобы закрыть сокет - его просто не рекомендуется.
На мой взгляд, это зависит от того, нужно ли время ожидания на время приложения обрабатывать сообщения в очереди. Если бы я был уверен, что мое клиентское приложение обработало все необходимые сообщения, то я, вероятно, либо дам ему время ожидания 0 секунд, либо, возможно, 1 секунду, если я подумаю, что это может измениться в будущем.
Для очень занятого клиента и/или слабого оборудования - тогда я мог бы дать ему больше времени. Для сервера мне пришлось бы сравнивать разные значения под нагрузкой.
Другие полезные ссылки:
What is the proper way of closing and cleaning up a Socket connection?
Are there any cases when TcpClient.Close or Socket.Close(0) could block my code?
«не должен ли GC также ссылаться на метод Dispose() объекта?» GC * never * вызывает 'Dispose'. Он вызывает только финализатор класса, если он есть. Обычно 'Dispose()' вызывается из финализатора, но это повторяется: 'Dispose' предназначен только для' use'. GC вообще не заботится об этом. –
Это отличное объяснение участия GC в этом, THX! – cogumel0
Обратите внимание, что ваш код не дает времени GC для фактического сбора сокетов, поскольку у них есть финализаторы. Вам нужно сделать 'GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); 'заставить это. Ваш вызов 'GC.Collect();' просто помещает сокеты в очередь финализатора (если это) - и финализаторы сокетов могут занимать много времени (часто убивая ваш процесс). Сокеты не находятся в TIME_WAIT, скорее всего, потому, что они еще не были закрыты *. То же самое происходит, когда вы убиваете процесс - на самом деле нетривиально отключить сокет TCP, даже для ОС. Перечислите все порты, а не только TIME_WAIT. – Luaan