为什么这个 CancellationDisposable 永远不会在 Observable.Dispose() 上被取消?

发布于 2024-12-16 17:58:04 字数 1379 浏览 1 评论 0原文

我在 WinForms 应用程序中使用 RxFramework。我尝试运行 Observable 异步并使用 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();
}

Im using RxFramework within an WinForms app. Im trying to run an Observable async and using the CancellationDisposable to cancel the operation when user clicks a button. but it is not working!

Suppose I have a form with 2 buttons and a ProgressBar. Button1_click subscribe to the observer on a new Thread. Button2_click is then pressed right after to cancel the operation.
Why cancel.Token.IsCancellationRequested is never true?

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();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

掐死时间 2024-12-23 17:58:04

之所以会发生这种情况,是因为您传递给 Observable.Create 的 lambda 不会返回 CancellationDisposable 直到它完成所有步骤。因此,操作的顺序如下:

  1. 您调用 Subscribe
  2. UI 线程块
  3. ThreadPool 线程上,执行进入
  4. 创建 lambda CancellationDisposable
  5. 您多次检查取消并执行整个过程。在此期间,您的进度条会更新。
  6. cancel 从 lambda
  7. UI 线程被释放返回,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...
    });

}

It happens so because the lambda you pass to Observable.Create does not return the CancellationDisposable until it goes through all the steps. So, the order of actions is the following:

  1. You call Subscribe
  2. UI thread blocks
  3. On a ThreadPool thread, the execution enters the lambda
  4. CancellationDisposable is created
  5. You check for cancellation several times and execute the whole process. During this, your progress bar is updated.
  6. cancel is returned from lambda
  7. UI thread is released and obs gets its value
  8. You click Button2 and call obs.Dispose
  9. cancel gets cancel.Token.IsCancellationRequested=true. But nothing happens since everything has already happened.

Here's the fixed code:

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...
    });

}
娇纵 2024-12-23 17:58:04

相反,上面的代码存在许多错误,但实际上有一种更好的方法来完成此操作(警告:前面在 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 */);

How about this instead, there are a number of bugs with the code above, but there's actually a better way to do this altogether (Warning: Coding in TextArea ahead):

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 */);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文