我有一个ASP.NET WebApi请求方法,该方法依次在遗留资源上使用Observable.Using
启动异步调用。此资源将生成一个新线程,在该线程上,它会引发事件,然后这些事件将转换为由资源周围的包装器公开的OnNext
流中的IObservable
项。
我await
使用IObservable.FirstOrDefaultAsync()
async
我的WebApi方法将async
标记为await
。
如果我采用这种设置,我会得到臭名昭着的
异步模块或处理程序 在异步操作仍未完成时完成
所以,我的第一个问题就是这个问题。我假设我已经得到了这个,因为遗留资源会产生新的异步操作(没有SynchronizationContext.Current._state.VoidAsyncOutstandingOperationCount
/ Dispose
),但ASP.NET究竟是如何知道的呢?注册了什么?我发现ASP.NET正在查看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();
以便引发此异常,但是哪个调用会增加此属性?线程排队到ThreadPool?我相当确定这是由遗留资源制作的。
现在为什么在我处理资源时这些操作仍然存在?好吧,似乎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());
使用Dispose
:
Observable.Using
这对我来说就像是一个黑客。
思考和建议?
我想我在这里遇到的一个问题是,当我使用{{1> 序列终止/完成后,资源的Dispose
方法被称为 }}。应该是这样的吗?在这种情况下,真的没有办法等待使用该构造的IObservable<Unit> Disposed()
。我必须使用额外的render
方法或类似的方法修改api ...
答案 0 :(得分:1)
Dispose
将在调用它的线程上调用(好吧)。更有帮助的是,在另一个调用OnComplete
回调的线程上消耗/观察Rx序列时。如果您将Rx与标准运算符一起使用,那么当序列终止时(使用OnError
或OnComplete
),您将获得自动处理行为。这种自动处理只会在OnComplete
/ OnError
之后发生,并且只会在同一个线程上运行。
如果您希望将处置绑定到调度程序,那么我建议您查看System.Reactive.Disposables.ScheduledDisposable
类型。但是,似乎在这里使用SynchronizationContext
更自然,因此在这种情况下System.Reactive.Disposables.ContextDisposable
可能更合适。