【问题标题】:Observable.Using disposes on other thread than created onObservable.Using 在其他线程上处理而不是在创建的线程上
【发布时间】:2016-02-09 16:09:45
【问题描述】:

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

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

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

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

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

现在为什么在我处置资源时这些操作仍在进行?好吧,看来Dispose 是在传播事件的线程上运行的,在概念上类似于下面的 sn-p。

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,而是使用返回给控制器的这个构造,awaiting 它使用FirstOrDefaultAsync()

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

不过,这对我来说就像是一种黑客行为。

想法和建议?

编辑 1

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

【问题讨论】:

  • 关于您的第一个问题,请发布堆栈跟踪。
  • @shay__:没有堆栈跟踪。 “异步模块或处理程序已完成,而异步操作仍处于挂起状态”是唯一显示的内容,如果您查看 IL 代码,它实际上并没有抛出任何异常(只是创建 InvalidOperationException,然后将其传递给某个异常处理程序事情。

标签: c# asp.net-web-api async-await system.reactive


【解决方案1】:

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

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

【讨论】:

  • System.Reactive.Disposables.ContextDisposable 很有趣!我要试试。但是,我仍然想知道在使用Observable.Using 时,在调用OnCompleted 之后难以确保Dispose 已运行的设计。或者我对此的理解可能存在根本缺陷。
  • 如果您担心序列已完成然后自动处理的用例;那么我假设DisposeOnComplete 完成运行后被调用。然而,这并不意味着Dispose 将始终跟随OnComplete。如果在序列完成之前终止订阅,您将看到没有OnComplete 的处置触发。
猜你喜欢
  • 2012-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-26
  • 1970-01-01
  • 1970-01-01
  • 2021-10-12
相关资源
最近更新 更多