C# 如何取消可观察序列

C# 如何取消可观察序列,c#,system.reactive,cancellation,C#,System.reactive,Cancellation,我有一个非常简单的IObservable,每500毫秒用作一个脉冲发生器: var pulses = Observable.GenerateWithTime(0, i => true, i => i + 1, i => i, i => TimeSpan.FromMilliseconds(500)) 我有一个CancellationTokenSource(用于取消同时进行的其他工作) 如何使

我有一个非常简单的
IObservable
,每500毫秒用作一个脉冲发生器:

var pulses = Observable.GenerateWithTime(0, i => true, i => i + 1, i => i,
                                         i => TimeSpan.FromMilliseconds(500))
我有一个
CancellationTokenSource
(用于取消同时进行的其他工作)


如何使用取消令牌源取消可观察序列?

您可以从订阅中获得一个
IDisposable
实例。对此调用
Dispose()

您可以将您的
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);

如果您使用的是GenerateWithTime(现在替换为在timespan func重载中传递Generate),则可以替换第二个参数来评估取消令牌的状态,如下所示:

var pulses = Observable.Generate(0,
    i => !ts.IsCancellationRequested,
    i => i + 1,
    i => i,
    i => TimeSpan.FromMilliseconds(500));
或者,如果可以将导致设置取消令牌的事件转换为可观察事件本身,则可以使用如下内容:

pulses.TakeUntil(CancelRequested);

我也在上发布了一个更详细的解释。

这是一个旧线程,但仅供将来参考,这里有一个更简单的方法

如果您有CancellationToken,您可能已经在处理任务了。因此,只需将其转换为任务,并让框架进行绑定:

using System.Reactive.Threading.Tasks;
...
var task = myObservable.ToTask(cancellationToken);
这将创建一个内部订阅服务器,该订阅服务器将在任务取消时释放。在大多数情况下,这会起到作用,因为大多数可观测数据只有在有订阅者的情况下才会产生值

现在,如果您有一个由于某种原因需要处理的实际可观察对象(如果父任务被取消,可能是一个不再重要的热可观察对象),这可以通过继续:

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

这里有两个用于取消可观测序列的简便运算符。它们之间的区别在于取消时会发生什么。
TakeUntil
导致序列正常完成(
OnCompleted
),而
with cancellation
导致异常终止(
OnError



注意:在取消的情况下,上述自定义操作符将立即从基础可观察对象取消订阅。在可观察到的包括副作用的情况下,这是要考虑的。将
TakeUntil(cts.Token)
放在执行副作用的操作员之前,将推迟整个可观察的完成,直到副作用完成()。将其放在副作用之后会使取消立即生效,从而可能导致任何正在运行的代码继续以一种不被观察到的方式运行。

我知道,但我需要有一些其他流程来轮询取消状态,并在取消发生时处理订阅。我在寻找一些更自动的东西,以某种方式将我的取消令牌源链接到我的可观察对象。你的答案和吉姆的答案都是实现这一点的有效方法。您的方法可以应用于所有类型的可观察对象,但Jim的方法在使用
Generate
方法时更为简洁。
/// <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)))));
}
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);