Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 配置IObservable的等待<;T>;_C#_Asynchronous_Async Await_System.reactive - Fatal编程技术网

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()
传递取消令牌以取消订阅。