Сохранение ссылок на `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(), если это необходимо.