可观察.使用在创建时以外的其他线程上释放

本文关键字:其他 线程 释放 观察 创建 | 更新日期: 2023-09-27 18:33:57

我有一个 ASP.NET 的WebApi请求方法,该方法反过来使用遗留资源上的Observable.Using启动异步调用。此资源将生成一个新线程,在该线程上引发事件,这些事件又转换为资源包装器公开的IObservable流中的OnNext项。

我正在使用IObservable.FirstOrDefaultAsync() await流的结果,我的 WebApi 方法标记为 async .

如果我采用这种设置,我会得到臭名昭著的

异步模块或处理程序 在异步操作仍处于挂起状态时完成

所以,我的第一个问题是关于这个的。我假设我得到了这个,因为新的异步操作(没有 async/await (是由遗留资源生成的,但 ASP.NET 究竟是如何知道这一点的?已注册了什么?我发现 ASP.NET 正在查看SynchronizationContext.Current._state.VoidAsyncOutstandingOperationCount以引发此异常,但是哪些调用会增加此属性?线程排队进入线程池?我相当确定这是由遗留资源做出的。

现在,为什么在我处置资源时这些操作仍在进行?好吧,它似乎Dispose在传播事件的线程上运行,在概念上类似于下面的代码片段。

Observable
    .Using(
        () => {
            Console.WriteLine($"Created on thread: {Thread.CurrentThread.ManagedThreadId}");
            return Disposable.Create(() => {
                Console.WriteLine($"Disposed on thread: {Thread.CurrentThread.ManagedThreadId}");
            });
        },
        _ => Observable.Return(1, NewThreadScheduler.Default))
    .Do(_ => Console.WriteLine($"OnNext on thread: {Thread.CurrentThread.ManagedThreadId}"))
    .Wait();

结果与此类似:

Created on thread: 10
OnNext on thread: 11
Disposed on thread: 11

这是设计使然吗?使用 using 释放资源时,很明显,资源被释放在创建它的同一线程上,因为代码是同步的。当Dispose在单独的线程上运行时,调用代码将继续运行,控制器将在Dispose完全完成之前返回(至少大多数时候

(。

我怎样才能以理智的方式缓解这种情况?一种似乎有效的方法是,而不是使用 Observable.Using ,使用返回给控制器的这个结构,await使用 FirstOrDefaultAsync() 来使用它:

var resource = // Creating resource manually.
return resource.StartAsyncOperation() // <- observable producing events
    .ObserveOn(SynchronizationContext.Current)
    .Do(_ => resource.Dispose());

不过,这对我来说就像一个黑客。

想法和建议?

编辑 1

我想我在这里面临的问题之一是,当我使用 Observable.Using 时,在序列终止/完成后调用资源的 Dispose 方法。应该这样吗?在这种情况下,实际上没有办法等待使用该结构的Dispose。我必须使用额外的IObservable<Unit> Disposed()方法或类似的东西来修改 api......

可观察.使用在创建时以外的其他线程上释放

Dispose将在调用它的线程上调用(嗯(。为了更有用,当 Rx 序列在另一个线程上使用/观察时,该线程将调用OnComplete回调。如果将 Rx 与标准运算符一起使用,则当序列终止(使用 OnErrorOnComplete(时,您将获得自动处置行为。这种自动处置将直接发生在OnComplete/OnError之后,并且只会在同一线程上运行。

如果您希望将处置绑定到调度程序,那么我建议您查看System.Reactive.Disposables.ScheduledDisposable类型。然而,在这里使用SynchronizationContext似乎更自然,所以在这种情况下,System.Reactive.Disposables.ContextDisposable可能更合适。