Почему этот CancellationDisposable никогда не отменяется в Observable.Dispose ()? - PullRequest
2 голосов
/ 17 ноября 2011

Я использую RxFramework в приложении WinForms.Я пытаюсь запустить Observable async и использую CancellationDisposable, чтобы отменить операцию, когда пользователь нажимает кнопку.Но это не работает!

Предположим, у меня есть форма с 2 кнопками и ProgressBar.Button1_click подписаться на наблюдателя в новой теме.Button2_click затем нажимается сразу после отмены операции.Почему cancel.Token.IsCancellationRequested никогда не соответствует действительности?

private IDisposable obs = null;
private void button1_Click(object sender, EventArgs e) {
    var countObserver = Observable.Create<int>(observer => {
        var cancel = new CancellationDisposable();

        if (!cancel.Token.IsCancellationRequested) {
            //Step 1 of a long running process using lot of resources...
            observer.OnNext(1);
        }
        if (!cancel.Token.IsCancellationRequested) {
            //Step 2 of a long running process using lot of resources...
            observer.OnNext(1);
        }
        if (!cancel.Token.IsCancellationRequested) {
            //Step 3 of a long running process using lot of resources...
            observer.OnNext(1);
        }
        observer.OnCompleted();

        return cancel;
    });

    obs = countObserver
        .ObserveOn(new ControlScheduler(this))
        .SubscribeOn(Scheduler.ThreadPool)
        .Subscribe(i => {
            //Update a progress bar here...
        });

}

private void button2_Click(object sender, EventArgs e) {
    if (obs != null)
        obs.Dispose();
}

Ответы [ 2 ]

3 голосов
/ 02 декабря 2011

Это происходит потому, что лямбда, которую вы передаете Observable.Create, не возвращает CancellationDisposable , пока не пройдет все шаги.Итак, порядок действий следующий:

  1. Вы вызываете Subscribe
  2. блоки потоков UI
  3. В потоке ThreadPool выполнение входит вlambda
  4. CancellationDisposable создан
  5. Вы проверяете отмену несколько раз и выполняете весь процесс.При этом ваш индикатор прогресса обновляется.
  6. cancel возвращается из лямбды
  7. Поток пользовательского интерфейса освобождается и obs получает свое значение
  8. Вы нажимаете Button2и звоните obs.Dispose
  9. cancel получает cancel.Token.IsCancellationRequested=true.Но ничего не происходит, так как все уже произошло.

Вот исправленный код:

private void button1_Click(object sender, EventArgs e) {
  var countObserver = Observable.Create<int>(observer => {
    var cancel = new CancellationDisposable();

    // Here's the magic: schedule the job in background and return quickly
    var scheduledItem = Scheduler.ThreadPool.Schedule(() =>
    {
      if (!cancel.Token.IsCancellationRequested) {
        //Step 1 of a long running process using lot of resources...
        observer.OnNext(1);
      }
      if (!cancel.Token.IsCancellationRequested) {
        //Step 2 of a long running process using lot of resources...
        observer.OnNext(1);
      }
      if (!cancel.Token.IsCancellationRequested) {
        //Step 3 of a long running process using lot of resources...
        observer.OnNext(1);
      }
      observer.OnCompleted();
    });

    return new CompositeDisposable(cancel, scheduledItem);
});

obs = countObserver
    .ObserveOn(new ControlScheduler(this))
    .Subscribe(i => {
        //Update a progress bar here...
    });

}
1 голос
/ 18 ноября 2011

Как насчет этого, есть ряд ошибок с кодом выше, но на самом деле есть лучший способ сделать это в целом (Внимание: кодирование в TextArea впереди):

countObservable = Observable.Timer(new ControlScheduler(this));

var buttonObservable = Observable.FromEventPattern<EventArgs>(
    x => button1.Click += x, x => button1.Click -= x);

countObservable
    .TakeUntil(buttonObservable)
    .Subscribe(x => /* Do stuff */);
...