C# 可观察。在其他线程上使用disposes,而不是在

C# 可观察。在其他线程上使用disposes,而不是在,c#,asp.net-web-api,async-await,system.reactive,C#,Asp.net Web Api,Async Await,System.reactive,我有一个ASP.NET WebApi请求方法,它依次使用Observable.using对遗留资源启动异步调用。此资源将生成一个新线程,在该线程上引发事件,这些事件依次转换为IObservable流中的OnNext项,该流由资源周围的包装器公开 我正在使用IObservable.FirstOrDefaultAsync()等待流的结果,我的WebApi方法被标记为async 如果我采用这种设置,我将得到臭名昭著的 异步模块或处理程序 异步操作仍挂起时已完成 所以,我的第一个问题是关于这个。我假设我

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

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

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

异步模块或处理程序 异步操作仍挂起时已完成

所以,我的第一个问题是关于这个。我假设我得到这个消息是因为新的异步操作(no
async
/
wait
)是由遗留资源生成的,但是ASP.NET如何确切地知道这一点呢?登记了什么?我发现ASP.NET正在查看SynchronizationContext.Current.\u 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。使用
,使用返回给控制器的这个构造,
使用
FirstOrDefaultAsync()
等待它:

var resource=//手动创建资源。
return resource.StartAsyncOperation()//resource.Dispose());
不过,这对我来说是一种折磨

想法和建议

编辑1
我想我在这里面临的一个问题是,当我使用
Observable.using
时,在序列终止/完成后调用资源的
Dispose
方法。应该是这样吗?在这种情况下,真的没有办法等待
Dispose
使用该构造。我必须用一个附加的
IObservable Disposed()
方法或类似的方法来修改api…

调用它的线程将调用
Dispose
(好吧)。更有用的是,当在另一个线程上使用/观察Rx序列时,将调用
OnComplete
回调。如果您将Rx与标准操作员一起使用,则当序列终止时,您将获得自动处理行为(使用
OnError
OnComplete
)。此自动处理将在
OnComplete
/
OnError
之后立即发生,并将在同一线程上运行


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

关于第一个问题,请发布堆栈跟踪。@shay\uuuu:没有堆栈跟踪。“异步模块或处理程序在异步操作仍挂起时完成”是唯一显示的内容,如果您查看IL代码,它实际上不会引发任何异常(只需创建
invalidoOperationException
,然后将其传递给某个异常处理程序。
System.Reactive.Disposables.ContextDisposable
很有趣!我将尝试一下。但是,我仍然想知道这样的设计是否难以确保
Dispose
完成后运行。)ed
在使用
Observable时被调用。使用
。或者在我的理解中,这可能是根本性的缺陷。如果您担心序列已完成,然后自动处置时的用例;那么我会假设
OnComplete
完成运行后会调用
Dispose
。但是,这并不意味着
Dispose
将始终遵循
OnComplete
。如果订阅在序列完成之前终止,您将看到没有
OnComplete
的Dispose火灾。
var resource = // Creating resource manually.
return resource.StartAsyncOperation() // <- observable producing events
    .ObserveOn(SynchronizationContext.Current)
    .Do(_ => resource.Dispose());