Как заметная подписка изящно прекращается?

2

Я пытаюсь использовать Reactive Extensions (Rx) для обработки потока данных. Однако обработка каждого элемента может занять некоторое время. Чтобы разбить обработку, я использую CancellationToken, который фактически останавливает подписку.

Когда была запрошена отмена, как мне изящно завершить текущую работу и закончить, не теряя при этом никаких данных?

Пример:

var cts = new CancellationTokenSource();
cts.Token.Register(() => Console.WriteLine("Token cancelled."));

var observable = Observable
    .Interval(TimeSpan.FromMilliseconds(250));

observable
    .Subscribe(
        value =>
            {
                Console.WriteLine(value);
                Thread.Sleep(500);

                if (cts.Token.IsCancellationRequested)
                {
                    Console.WriteLine("Cancellation detected on {0}.", value);
                    Thread.Sleep(500);
                    Console.WriteLine("Cleaning up done for {0}.", value);
                }
            },
        () => Console.WriteLine("Completed"),
        cts.Token);

Console.ReadLine();
cts.Cancel();
Console.WriteLine("Job terminated.");

Выход:

0
1
2
Token cancelled.
Job terminated.
Cancellation detected on 2.
Cleaning up done for 2.

Как видно из вывода, строка "Job terminated" не является последней строкой, а это означает, что у очистки не было бы достаточно времени для завершения до того, как приложение завершится.

  • 0
    Что бы вы хотели получить на выходе?
  • 0
    В этом примере поток приложения завершается раньше, чем подписка. Я не могу найти способ каким-то образом дождаться, пока обработчик подписки OnNext завершит свою работу. Я не хочу, чтобы приложение закрывалось до завершения обработчика, поэтому «Задание прекращено» должно быть последним отпечатком.
Теги:
cancellation
system.reactive

2 ответа

2
Лучший ответ

Используя ответ на аналогичный вопрос вместе с ответом на вопрос о ожидании перед завершением, я выяснил решение, которое делает то, что я хочу.

Моя первоначальная проблема заключалась в том, что я не нашел возможности ждать потока подписки. Ответы, приведенные выше, привели меня к рефакторингу кода тремя способами:

  1. Я переместил логику отмены от подписки на наблюдаемую.

  2. Подписка завершается в свою собственную Task (поэтому выполнение может продолжаться ReadLine -statement).

  3. Для управления стратегией выхода из приложения был введен ManualResetEvent.

Решение:

var reset = new ManualResetEvent(false);

var cts = new CancellationTokenSource();
cts.Token.Register(() => Console.WriteLine("Token cancelled."));

var observable = Observable
    .Interval(TimeSpan.FromMilliseconds(250))
    .TakeWhile(x => !cts.Token.IsCancellationRequested)
    .Finally(
        () =>
            {
                Console.WriteLine("Finally: Beginning finalization.");
                Thread.Sleep(500);
                Console.WriteLine("Finally: Done with finalization.");
                reset.Set();
            });

await Task.Factory.StartNew(
    () => observable
        .Subscribe(
            value =>
                {
                    Console.WriteLine("Begin: {0}", value);
                    Thread.Sleep(2000);
                    Console.WriteLine("End: {0}", value);
                },
            () => Console.WriteLine("Completed: Subscription completed.")),
    TaskCreationOptions.LongRunning);

Console.ReadLine();
cts.Cancel();
reset.WaitOne();
Console.WriteLine("Job terminated.");

Выход:

Begin: 0
End: 0
Begin: 1
Token cancelled.
End: 1
Completed: Subscription completed.
Finally: Beginning finalization.
Finally: Done with finalization.
Job terminated.

Будучи совершенно новым для Reactive Extensions, я не знаю, является ли это лучшим решением для моей проблемы. Но это отличный пример для примера, поставленного в вопросе, поскольку он удовлетворяет моим требованиям:

  • Каждому действию OnNext разрешено работать до завершения.
  • Приложение ожидает завершения обработки потока (с сообщением ManualResetEvent).
  • Логика отмены потока перемещается производителю (вместо потребителя) в TakeWhile -method.
  • Логика завершения приложения - это реакция на отмену потока у производителя Finally -method.

Это гораздо более приятное решение.

2

Если я правильно понял вопрос, это не проблема Rx, это "Что бы вы ни делали в проблеме" Подписаться ". Действие вашей подписки занимает полсекунды, а возможность очистки занимает еще полсекунды, а ваше завершение работы занимает микросекунды. Что вы хотите сжать между отменой и прекращением?

Лучшим советом, который я могу вам дать, является то, что действие подписки соблюдает маркер отмены лучше, чем призывы Thread.Sleep.

  • 0
    Я надеюсь на какой-то механизм, который позволит приложению ожидать завершения работы обработчика подписки OnNext.
  • 0
    @Reyhn - Используйте 1WaitHandle`.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню