Ответ 1
Это не имеет большого значения для меня, так как это объект IDisposable, не должен ли GC также ссылаться на метод Dispose() объекта?
Dispose pattern, также известный как IDisposable, предоставляет два способа очистки неуправляемого объекта. Метод Dispose обеспечивает прямой и быстрый способ очистки ресурса. Метод finalize, который вызывается сборщиком мусора, является отказоустойчивым способом удостовериться, что неуправляемый ресурс очищается, если другой разработчик, использующий код, забывает вызвать метод Dispose. Это несколько похоже на то, что разработчики С++ забывают вызывать Delete в памяти, выделенной кучей, что приводит к утечкам памяти.
Согласно ссылочной ссылке:
"Хотя финализаторы эффективны в некоторых сценариях очистки, у них есть два существенных недостатка:
-
Финализатор вызывается, когда GC обнаруживает, что объект имеет право на сбор. Это происходит в некоторый неопределенный период времени после того, как ресурс больше не нужен. Задержка между тем, когда разработчик может или хочет освободить ресурс, и время, когда ресурс фактически выпущен финализатором, может быть неприемлемым в программах, которые приобретают множество скудных ресурсов (ресурсы, которые можно легко истощать) или в случаях, когда ресурсы (например, большие неуправляемые буферы памяти).
-
Когда CLR нужно вызвать финализатор, он должен отложить сбор памяти объектов до следующего раунда сборки мусора (финализаторы выполняются между коллекциями). Это означает, что память объектов (и все объекты, на которые она ссылается) не будет выпущена в течение более длительного периода времени.
Используя этот метод, сокеты никогда не входят в состояние TIME_WAIT. То же самое, если я просто закрою приложение, прежде чем вручную вызывать Close() или Dispose()
Может кто-то пролить свет и объяснить, будет ли это хорошей практикой (что, я думаю, люди скажут это не так).
Причина, по которой он задерживается, заключается в том, что код задерживается по умолчанию, чтобы дать приложению некоторое время для обработки любые сообщения в очереди. В соответствии с TcpClient.Close метод doc в MSDN:
"Метод Close помещает экземпляр как расположенный и запрашивает, чтобы связанный Socket закрывал TCP-соединение. Основываясь на свойстве LingerState, TCP-соединение может оставаться открытым в течение некоторого времени после вызова метода Close, когда данные остаются отправленными. Нет уведомления, предоставленного, когда базовое соединение завершило закрытие.
Вызов этого метода в конечном итоге приведет к закрытию связанного Socket и также закроет связанный с ним NetworkStream, который используется для отправки и получения данных, если он был создан. "
Это значение таймаута может быть уменьшено или полностью устранено с помощью следующего кода:
// 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 на null, рекомендуется использовать метод Close. Когда для ссылки задано значение null, GC заканчивает вызов метода finalize. Метод finalize в конечном итоге вызывает метод Dispose для консолидации кода для очистки неуправляемого ресурса. Таким образом, он будет работать, чтобы закрыть сокет - его просто не рекомендуется.
На мой взгляд, это зависит от того, нужно ли время задержки на время приложения обрабатывать сообщения в очереди. Если бы я был уверен, что мое клиентское приложение обработало все необходимые сообщения, я бы, вероятно, либо дал ему время ожидания 0 секунд или, возможно, 1 секунду, если бы я думал, что это может измениться в будущем.
Для очень занятого клиента и/или слабого оборудования - тогда я могу дать ему больше времени. Для сервера мне пришлось бы сравнивать разные значения под нагрузкой.
Другие полезные ссылки:
Каков правильный способ закрытия и очистки соединения Socket?
Есть ли случаи, когда TcpClient.Close или Socket.Close(0) могут блокировать мой код?