使用 System.Reactive 执行同步方法,但有超时

问题描述 投票:0回答:2
调用超时的

Read

 方法的正确方法是什么?

public int Method() { return Read(); }
我已经尝试过这样做,但是当超时发生时,我的应用程序会完全关闭,所以我猜它有问题。

public int Method() { return Observable.Start(() => Read()).Timeout(TimeSpan.FromSeconds(2)).Wait(); }
    
c# .net async-await reactive-programming system.reactive
2个回答
1
投票
您应该避免使用

Timeout

,因为它确实通过异常进行控制,我们知道这是不好的。

我倾向于使用这种模式:

Observable .Amb( Observable.Timer(TimeSpan.FromSeconds(2.0)).Select(x => (int?)null), Observable.Start(() => (int?)Read()))

Amb

 在第一个可观察量上触发以产生一个值,然后忽略另一个。只需设置一个适当的返回值,在您的情况下,它是 
int?
 允许号码通过,或者是 
null
 如果超时。

然后我利用我们可以等待可观察值的最后一个值这一事实,我这样做了:

public async Task<int?> Method() => await Observable .Amb( Observable.Timer(TimeSpan.FromSeconds(2.0)).Select(x => (int?)null), Observable.Start(() => (int?)Read())) .LastAsync();
如果你不想使用任务那么你仍然可以使用

Wait()

:

public int? Method() => Observable .Amb( Observable.Timer(TimeSpan.FromSeconds(2.0)).Select(x => (int?)null), Observable.Start(() => (int?)Read())) .Wait();
    

0
投票
在您发布的代码中,

Wait

 方法会阻塞,直到 
Observable
 完成或引发异常。如果超时,
Timeout
运算符将抛出 
TimeoutException
。由于 
Wait
 不处理异常,它会传播此异常,导致应用程序崩溃。

您可以使用

Subscribe

 代替 
Wait
 并处理异常:

public int Method() { int result = 0; var resetEvent = new AutoResetEvent(false); Observable .Start(() => Read()) .Timeout(TimeSpan.FromSeconds(2)) .Subscribe( x => { result = x; resetEvent.Set(); }, // onNext ex => { /* handle exception */ resetEvent.Set(); } // onError ); resetEvent.WaitOne(); // Wait until the Observable completes or an exception is thrown return result; }
此代码启动一个包装 

Observable

 方法的 
Read
。它要么完成并返回结果(分配给 
result
),要么失败并返回 
TimeoutException
。无论哪种情况,都会向 
AutoResetEvent
 发出信号,从而允许 
WaitOne
 方法完成并允许 
Method
 方法返回结果。

确保将

/* handle exception */

 替换为您自己的异常处理代码。例如,您可以将 
result
 设置为指示超时的特殊值,或者您可以记录异常。

最后一件事:由于

Method

resetEvent.WaitOne()
 仍然处于阻塞状态。这对于控制台应用程序来说可能没问题,但在 GUI 应用程序中,这会冻结 UI,直到 
Read
 方法完成或发生超时。如果您想避免阻塞,则需要使用不同的设计,例如使 
Method
 异步或提供在 
Read
 方法完成时调用的回调。

© www.soinside.com 2019 - 2024. All rights reserved.