WedX - журнал о программировании и компьютерных науках

Почему RefCount не работает после отключения всех первоначальных подписчиков?

Рассмотрим следующее:

[Fact]
public void foo()
{
    var result = new Subject<bool>();
    var startCount = 0;
    var completionCount = 0;
    var obs = Observable
        .Defer(() =>
            {
                ++startCount;
                return result.FirstAsync();
            })
        .Do(_ => ++completionCount)
        .Publish()
        .RefCount();

    // pretend there are lots of subscribers at once
    var s1 = obs.Subscribe();
    var s2 = obs.Subscribe();
    var s3 = obs.Subscribe();

    // even so, we only expect to be started once
    Assert.Equal(1, startCount);
    Assert.Equal(0, completionCount);

    // and we won't complete until the result ticks through
    result.OnNext(true);
    Assert.Equal(1, startCount);
    Assert.Equal(1, completionCount);

    s1.Dispose();
    s2.Dispose();
    s3.Dispose();

    // now try exactly the same thing again
    s1 = obs.Subscribe();
    s2 = obs.Subscribe();
    s3 = obs.Subscribe();

    // startCount is 4 here instead of the expected 2!
    Assert.Equal(2, startCount);
    Assert.Equal(1, completionCount);

    result.OnNext(true);
    Assert.Equal(2, startCount);
    Assert.Equal(2, completionCount);

    s1.Dispose();
    s2.Dispose();
    s3.Dispose();
}

Мое понимание Publish + RefCount заключается в том, что соединение с источником сохраняется до тех пор, пока есть хотя бы один подписчик. Как только последний подписчик отключится, любой будущий подписчик повторно инициирует подключение к источнику.

Как вы можете видеть в моем тесте, все работает отлично с первого раза. Но во второй раз отложенная наблюдаемая внутри пайплайна выполняется один раз для каждого нового подписчика.

С помощью отладчика я вижу, что для первой группы подписчиков obs._count (которая считается подписчиками) увеличивается с каждым вызовом до Subscribe. А вот для второй группы абонентов она остается нулевой.

Почему это происходит и что я могу сделать, чтобы исправить мой конвейер?

29.02.2016

Ответы:


1

Ответ от @user631090 близок, но неверен, поэтому я решил ответить сам.

Это потому, что Publish немедленно завершит новых подписчиков, если поток, который он опубликовал, сам завершился. Вы можете увидеть это на диаграмме здесь:

введите здесь описание изображения

Но было бы неплохо, если бы на диаграмме появился подписчик после завершения базового потока.

Чтобы добавить путаницы, Defer по-прежнему вызывается для новых подписчиков. Но его возвращаемое значение просто игнорируется Publish из-за завершения исходного потока.

Я пока не могу придумать способ реализации моего предполагаемого варианта использования. Я подумал, что, возможно, использую Multicast, а не Publish, создавая новую тему по мере необходимости. Но я пока не смог этого добиться. И это кажется довольно болезненным для того, что я считаю распространенным вариантом использования.

29.02.2016
  • Кент, не могли бы вы объяснить предполагаемый вариант использования (в другом посте)? Возможно, сообщество может помочь вам более непосредственно там. (потенциально уменьшив количество движущихся частей: Тема + Отсрочка + Первый + Публикация + Счетчик ссылок и указав проблему (не ошибку), мы можем помочь больше. 01.03.2016
  • Конечно Ли. Я только что опубликовал этот вопрос в качестве продолжения: stackoverflow.com/questions/35762063/ 03.03.2016

  • 2

    Это потому, что базовый наблюдаемый результат уже завершен. Таким образом, каждый новый подписчик просто получает обратный вызов OnCompleted.

    Если бы ObservableDefer каждый раз создавал новую последовательность или ту, которая не была завершена, вы бы увидели желаемое поведение.

    e.g.

    return result.FirstAsync().Concat(Observable.Never<bool>());
    

    Вам нужно будет удалить Assert.Equal(1, completionCount);

    29.02.2016
  • Это звучит правдоподобно, но у меня возникли трудности с созданием последовательности, которая работала бы так, как ожидалось. Я думал, что сработает return result.Take(1);, а не return result.FirstAsync();, но я получаю тот же результат. Довольно любопытно., 29.02.2016
  • result не завершена. Каждый отдельный вызов result.FirstAsync будет завершен, когда будет отмечено новое значение. 29.02.2016
  • Я должен был сказать, что результат.FirstAsync завершен. Согласен, поведение странное. Похоже, это каким-то образом захвачено, поэтому будущие подписчики получают обратно завершенную наблюдаемую. Вы можете увидеть это, подключив свой второй набор событий OnCompleted подписчиков, они срабатывают немедленно, а не ждут, пока вы отправите другой .OnNext. Следовательно, каждый новый подписчик увеличивает счетчик. 29.02.2016
  • result.FirstAsync тоже не завершено. Даже если я создам совершенно новый Subject<bool> в обработчике Defer, я получу такое же поведение. Так что это больше похоже на то, что Defer правильно вызывается для получения следующего наблюдаемого, но затем игнорируется. Возможно, ошибка с Publish/RefCount? 29.02.2016
  • Новые материалы

    Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
    Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

    Лицензии с открытым исходным кодом: руководство для разработчиков и создателей
    В динамичном мире разработки программного обеспечения открытый исходный код стал мощной парадигмой, способствующей сотрудничеству, инновациям и прогрессу, движимому сообществом. В основе..

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..


    Для любых предложений по сайту: [email protected]