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