简体   繁体   English

如何取消可观察序列

[英]How to cancel an observable sequence

I have a very simple IObservable<int> that acts as a pulse generator every 500ms:我有一个非常简单的IObservable<int>每 500 毫秒充当一个脉冲发生器:

var pulses = Observable.GenerateWithTime(0, i => true, i => i + 1, i => i,
                                         i => TimeSpan.FromMilliseconds(500))

And I have a CancellationTokenSource (that is used to cancel other work that is going on simultaneously).我有一个CancellationTokenSource (用于取消同时进行的其他工作)。

How can I use the cancellation token source to cancel my observable sequence?如何使用取消令牌源取消我的可观察序列?

It is an old thread, but just for future reference, here is a simpler way to do it.这是一个旧线程,但仅供将来参考,这是一种更简单的方法。

If you have a CancellationToken, you are probably already working with tasks.如果您有一个 CancellationToken,那么您可能已经在处理任务。 So, just convert it to a Task and let the framework do the binding:因此,只需将其转换为 Task 并让框架进行绑定:

using System.Reactive.Threading.Tasks;
...
var task = myObservable.ToTask(cancellationToken);

This will create an internal subscriber that will be disposed when the task is cancelled.这将创建一个内部订阅者,该订阅者将在任务取消时被释放。 This will do the trick in most cases because most observables only produce values if there are subscribers.在大多数情况下,这将起到作用,因为大多数可观察对象仅在有订阅者时才产生值。

Now, if you have an actual observable that needs to be disposed for some reason (maybe a hot observable that is not important anymore if a parent task is cancelled), this can be achieved with a continuation:现在,如果您有一个实际的 observable 需要因某种原因被处置(如果父任务被取消,可能是一个不再重要的 hot observable),这可以通过延续来实现:

disposableObservable.ToTask(cancellationToken).ContinueWith(t => {
    if (t.Status == TaskStatus.Canceled)
        disposableObservable.Dispose();
});

If you're using the GenerateWithTime (replaced now with Generate passing in a timespan func overload), you can replace the second parameter to evaulate the state of the cancellation token as follows:如果您使用的是 GenerateWithTime(现在替换为 Generate 传入时间跨度函数重载),则可以替换第二个参数来评估取消令牌的 state,如下所示:

var pulses = Observable.Generate(0,
    i => !ts.IsCancellationRequested,
    i => i + 1,
    i => i,
    i => TimeSpan.FromMilliseconds(500));

Alternatively, if your event which causes the cancellation token to be set can be converted to an observable itself, you could use something like the following:或者,如果您的导致设置取消令牌的事件可以转换为可观察对象本身,您可以使用以下内容:

pulses.TakeUntil(CancelRequested);

I posted a more detailed explanation at http://www.thinqlinq.com/Post.aspx/Title/Cancelling-a-Reactive-Extensions-Observable as well.我也在http://www.thinqlinq.com/Post.aspx/Title/Cancelling-a-Reactive-Extensions-Observable上发布了更详细的解释。

You can connect your IObservable subscription with CancellationTokenSource with the following snippet您可以使用以下代码段将您的IObservable订阅与CancellationTokenSource连接起来

var pulses = Observable.GenerateWithTime(0,
    i => true, i => i + 1, i => i,
    i => TimeSpan.FromMilliseconds(500));

// Get your CancellationTokenSource
CancellationTokenSource ts = ...

// Subscribe
ts.Token.Register(pulses.Subscribe(...).Dispose);

Here are two handy operators for canceling observable sequences.这里有两个方便的运算符用于取消可观察序列。 The difference between them is on what happens in case of cancellation.它们之间的区别在于取消时会发生什么。 The TakeUntil causes a normal completion of the sequence ( OnCompleted ), while the WithCancellation causes an exceptional termination ( OnError ). TakeUntil导致序列正常完成( OnCompleted ),而WithCancellation导致异常终止( OnError )。

/// <summary>Returns the elements from the source observable sequence until the
/// CancellationToken is canceled.</summary>
public static IObservable<TSource> TakeUntil<TSource>(
    this IObservable<TSource> source, CancellationToken cancellationToken)
{
    return source
        .TakeUntil(Observable.Create<Unit>(observer =>
            cancellationToken.Register(() => observer.OnNext(default))));
}

/// <summary>Ties a CancellationToken to an observable sequence. In case of
/// cancellation propagates an OperationCanceledException to the observer.</summary>
public static IObservable<TSource> WithCancellation<TSource>(
    this IObservable<TSource> source, CancellationToken cancellationToken)
{
    return source
        .TakeUntil(Observable.Create<Unit>(o => cancellationToken.Register(() =>
            o.OnError(new OperationCanceledException(cancellationToken)))));
}

Usage example:使用示例:

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));

var pulses = Observable
    .Generate(0, i => true, i => i + 1, i => i, i => TimeSpan.FromMilliseconds(500))
    .WithCancellation(cts.Token);

Note: In case of cancellation, the custom operators presented above are unsubscribing instantly from the underlying observable.注意:在取消的情况下,上面介绍的自定义操作符会立即从底层 observable 取消订阅。 This is something to consider in case the observable includes side-effects.如果 observable 包含副作用,则需要考虑这一点。 Putting the TakeUntil(cts.Token) before the operator that performs the side-effects will postpone the completion of the whole observable, until the side-effects are completed ( graceful termination ).TakeUntil(cts.Token)放在执行副作用的操作符之前将推迟整个 observable 的完成,直到副作用完成( 优雅终止)。 Putting it after the side-effects will make the cancellation instantaneous, resulting potentially to any running code to continue running unobserved, in a fire-and-forget fashion.将其放在副作用之后将使取消立即生效,从而可能导致任何正在运行的代码以一种即发即弃的方式继续运行而不被观察。

You get an IDisposable instance back from subscribing.您从订阅中得到一个IDisposable实例。 Call Dispose() on that.调用Dispose()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM