C# 配置IObservable的等待<;T>;
当我将阻塞代码与C# 配置IObservable的等待<;T>;,c#,asynchronous,async-await,system.reactive,C#,Asynchronous,Async Await,System.reactive,当我将阻塞代码与Task.Wait()一起使用时,我遇到了死锁,正在等待内部等待Rx LINQ查询的async方法 这是一个例子: public void BlockingCode() { this.ExecuteAsync().Wait(); } public async Task ExecuteAsync() { await this.service.GetFooAsync().ConfigureAwait(false); //This is the
Task.Wait()
一起使用时,我遇到了死锁,正在等待内部等待Rx LINQ查询的async
方法
这是一个例子:
public void BlockingCode()
{
this.ExecuteAsync().Wait();
}
public async Task ExecuteAsync()
{
await this.service.GetFooAsync().ConfigureAwait(false);
//This is the RX query that doesn't support ConfigureAwaitawait
await this.service.Receiver
.FirstOrDefaultAsync(x => x == "foo")
.Timeout(TimeSpan.FromSeconds(1));
}
因此,我的问题是,是否存在可等待IOObservable上的ConfigureAwait的任何等价物,以确保不会在相同的
同步上下文上继续执行ConfigureAwait
与等待者本身没有直接关系,相反,TPL的一项功能是配置任务应如何完成。这是有问题的,因为此TPL方法不返回新的任务
,因此您无法将其转换为可观察任务
Rx本身基本上是自由线程的。您可以使用比任务
更精细的控制来控制订阅和事件期间使用的线程-有关此方面的详细信息,请参阅此处:
很难修复代码,因为您没有提供一个小而完整的工作示例-但是,Rx中的内置函数永远不会尝试马歇尔到特定线程,除非您特别告诉它们使用上述操作符之一
如果将Observable.FromAsync
之类的运算符组合在一起以将任务
转换为可观测,则可以使用Observable.SubscribeOn(Scheduler.Default)
从当前同步上下文启动任务
这里有一个要点(专为LINQPad设计,使用nuget package rx main运行):您必须理解“等待可观察”的含义。退房所以,从语义上来说,你的代码
await this.service.Receiver
.FirstOrDefaultAsync(x => x == "foo")
.Timeout(TimeSpan.FromSeconds(1));
相当于
await this.service.Receiver
.FirstOrDefaultAsync(x => x == "foo")
.Timeout(TimeSpan.FromSeconds(1))
.LastAsync()
.ToTask();
(注意这里有某种冗余,调用FirstOrDefaultAsync
和LastAsync
,但这没有问题)
因此,您得到了您的任务(如果可用,可能需要额外的CancellationToken
)。您现在可以使用configurewait
您永远不应该使用Task.Wait()
,并且应该避免使用Task.Result
。除非您知道自己在做什么,否则它们最终会导致死锁……但如果您知道,您也不会使用它们。我知道这一点,这就是为什么在我的示例中添加ConfigureAwait(false),它可以防止任何死锁,因为它不会在与Wait调用之前相同的SynchronizationContext上恢复。问题是,对于等待的可观察对象,ConfigureAwait(false)不存在……是的,我也用ObserveOn(NewThreadScheduler.Default)尝试过。但是在另一个线程中继续的是Subscribe方法中的委托。我不确定等待块之后的情况是否也会继续。比如://这里我在一个特定的Sinchronization上下文上等待observable.ObserveOn(NewThreadScheduler.Default).Subscribe(()=>{//这在另一个线程中是肯定的。})//这里,我是在什么样的同步上下文上?在等待之前我不想在同一台上……那么我就不能帮你这个.service.Receiver
。这完全取决于实施情况。我认为这没有抓住问题的关键。这不是关于观察到值的上下文,而是关于安排继续的上下文。我认为这并不是没有抓住要点。我是说FromAsync和SubscribeOn让您有机会控制计划继续的上下文。(在您链接的文章中,Paul提到使用StartAsync和Defer(来自Async wraps)的方法与此基本相同)OP试图避免由于编组回启动任务的SynchronizationContext(ala ConfigureAwait(true))而导致的死锁,因此,确保任务启动时不在上下文中可以解决问题。对于“启动”的任务,您指的是ExecuteAsync
?当然,确保已在某些线程池线程上启动is可以解决此特定问题,但这只会隐藏ExecuteAsync
中的真正问题,即继续在原始上下文中安排…这就是为什么我要求提供完整的工作程序,最好在该点解决问题,而不是在Rx中。我想我本可以更清楚一些。这是一个很好的解决办法。谢谢丹尼尔。我不明白为什么我需要直接执行.LastAsync().ToTask()而不是.ToTask()。你是对的,你不必这样做,因为ToTask已经获取了序列中的最后一个值。我只是想弄清楚什么代码等同于“等待一个可观察的”,并强调它始终是最后一个值。注意,您可以使用ToTask()
传递取消令牌以取消订阅。