Сохранение ссылок на `IDisposable` при использовании Reactive Extensions for.NET: всегда, никогда или иногда?
До сих пор я ревностно сохранял каждую ссылку на IDisposable
, возвращенный из любого метода .Subscribe(...)
, .Connect(...)
и т.д. внутри Rx. Я сделал это из-за своего опасения, что сбор мусора будет располагать одноразовым, если я не буду ссылаться на него.
Однако, я сделал тест в LINQPad, где я сделал несколько вызовов GC.Collect()
на .Subscribe(...)
, где я не сохранил ссылку и не понял, что? Мир не закончился, и подписка завершилась.
В ходе дальнейшего тестирования я обнаружил, что моя подписка была удалена сразу после .OnComplete()
без моего вмешательства.
Это приводит меня к пониманию того, что, по крайней мере, для .Subscribe(...)
, единственной причиной сохранения ссылки на подписку является принудительное завершение подписки до ее нормального завершения. Это больше похоже на токен отмены.
Итак, все ли Rx одноразовые используются для отмены, а не для сохранения?
Что же такое правила для подвешивания к IDisposable
?
Ответы
Ответ 1
IDisposable
, возвращаемый IScheduler.Schedule()
, полезен, если вы хотите отменить запланированное действие (то есть до того, как оно произошло)
IDisposable
, возвращаемый IObservable.Subscribe
и IConnectableObservable.Connect
, эквивалентен, в том, что распоряжение либо прекратит подписку на наблюдаемый источник.
Что касается сбора мусора, в то время как Rx делает его немного сложнее измерить, он все еще связан правилами ГК. Если источник вашего наблюдаемого коренится (например, событие элемента управления пользовательского интерфейса), вам не нужно беспокоиться о том, что он GC'd.
Если ваш источник является Observable.Interval
, или что-то еще в основном является рекурсивным вызовом IScheduler
, тогда планировщик должен поддерживать его в живом состоянии (то есть пул потоков, пул задач или диспетчер), поскольку наблюдаемый внедряется в планировщик (через IScheduler.Schedule
).
Ответ 2
Не нужно удерживать объекты IDisposable, если вы не хотите отменить подписку на наблюдаемый источник в будущем. Аналогично для методов расписания в IScheduler, чьи возвращенные объекты IDisposable могут использоваться для отмены запланированного действия.
Сборщик мусора не заботится о объекте IDisposable напрямую, и мы не реализуем финализаторы на любом из наших объектов, поэтому в основном управление подписками и т.д. полностью зависит от вас в мире Rx. Сравните это с Win32-дескрипторами, если хотите, с Dispose, являющимся моральным эквивалентом CloseHandle.
Общая информация: в какой-то момент во время проектирования Rx отмененные операции возвращают действие, действие которого приведет к аннулированию. Совершенно функционально вдохновленный природой, но менее очевидный для некоторых. Итак, мы решили пойти с интерфейсом, который уже представляет собой понятие управления ресурсами, а IDisposable - очевидный выбор. Это говорит о том, что он немного отличается от обычного использования интерфейса в другом месте в .NET Framework:
- Вы часто просто бросаете объект IDisposable на пол, в частности, для бесконечных последовательностей, от которых вы никогда не откажетесь.
- В большинстве случаев вы не будете использовать инструкцию using для объектов Rx IDisposable из-за присущего асинхронному характеру структуры.
Надеюсь, что это поможет,
-Bart (команда Rx)
Ответ 3
Хорошо, во-первых, я думаю, что важно указать, что сбор мусора и удаление объектов/интерфейс IDisposable
полностью разделены - в частности сборщик мусора никогда не будет напрямую распоряжаться объектом, вызвав метод Dispose
a IDisposable
(если финализатор для этого объекта делает это сам).
Что касается того, когда вы должны входить в IDisposable
, вы должны поддерживать ссылку на любой объект IDisposable
, который вам нужно утилизировать, - похоже, я заявляю очевидное, и это потому, что я!:-). Если срок жизни одноразовых объектов больше одного метода, обычно используется ключевое слово using
:
using (var myDisposableObject = GetSomeDisposableObject())
{
myDisposableObject.DoThings();
}
Это ограничивает область действия myDisposableObject
(что помогает избежать попытки использовать объект, который был удален) и гарантирует, что объект будет правильно удален, даже если выбрано исключение.
Это может быть тем, что для определенного класса /API вам не нужно (или не должно) удалять объект, который вам возвращен, однако это полностью зависит от API/class, который возвратил этот одноразовый объект.
Ответ 4
Да, IDisposable
, который возвращается из Subscribe()
, требуется только для отмены подписки(). Конечно, вызов Unsubscribe() не существует, вместо этого вы бы Dispose()
, если это необходимо.