C# 接收:等待第一项一段时间
我想将我遗留的基于事件的方法转换为基于可观察的方法,但我对Rx非常陌生,所以我现在被卡住了 我有一个事件源,它现在是可观察的。在某个时间点,我必须启动一个方法,该方法要么返回行中的下一个元素,要么在超时时返回null 基于事件的方法如下所示:C# 接收:等待第一项一段时间,c#,task-parallel-library,system.reactive,C#,Task Parallel Library,System.reactive,我想将我遗留的基于事件的方法转换为基于可观察的方法,但我对Rx非常陌生,所以我现在被卡住了 我有一个事件源,它现在是可观察的。在某个时间点,我必须启动一个方法,该方法要么返回行中的下一个元素,要么在超时时返回null 基于事件的方法如下所示: public async Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor) { ReaderEvent result = null; usin
public async Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
ReaderEvent result = null;
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(new [] { topLevelToken }))
{
cts.CancelAfter(waitFor);
EventHandler<ReaderEvent> localHandler = (o, e) =>
{
if (e.PlaceId == PlaceId)
{
result = e;
cts.Cancel();
}
};
ReaderEventHandler += localHandler;
try
{
await Task.Delay(waitFor, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
//...
}
ReaderEventHandler -= localHandler;
}
return result;
}
public异步任务WaitForReaderAsync(int-PlaceId,TimeSpan-waitFor)
{
ReaderEvent结果=null;
使用(var cts=CancellationTokenSource.CreateLinkedTokenSource(新[]{topLevelToken}))
{
cts.CancelAfter(waitFor);
EventHandler localHandler=(o,e)=>
{
如果(e.PlaceId==PlaceId)
{
结果=e;
cts.Cancel();
}
};
ReaderEventHandler+=localHandler;
尝试
{
等待任务。延迟(waitFor,cts.Token)。配置等待(false);
}
捕获(操作取消异常){}
捕获(例外情况除外)
{
//...
}
ReaderEventHandler-=localHandler;
}
返回结果;
}
正如您所看到的,这个想法是,延迟被我正在等待的事件的到达所取消,或者令牌源在特定的时间量之后被配置所取消。很干净
现在,Rx版本:
public async Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
ReaderEvent result = null;
var observable = _OnReaderEvent.FirstAsync(r => r.PlaceId == PlaceId);
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(new [] { topLevelToken }))
{
cts.CancelAfter(waitFor);
using (observable.Subscribe(x => {
result = x;
cts.Cancel();
{
try
{
await Task.Delay(waitFor, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
}
}
return result;
}
public异步任务WaitForReaderAsync(int-PlaceId,TimeSpan-waitFor)
{
ReaderEvent结果=null;
var observable=_OnReaderEvent.FirstAsync(r=>r.PlaceId==PlaceId);
使用(var cts=CancellationTokenSource.CreateLinkedTokenSource(新[]{topLevelToken}))
{
cts.CancelAfter(waitFor);
使用(可观察的)订阅(x=>{
结果=x;
cts.Cancel();
{
尝试
{
等待任务。延迟(waitFor,cts.Token)。配置等待(false);
}
捕获(操作取消异常){}
}
}
返回结果;
}
不太干净…更糟糕的是。。。
我也尝试过超时扩展。但由于这是一次性订阅,我仍然需要等待,然后才能处理订阅。唯一的区别是OneRor将取消本地令牌,而不是CancelAfter的内置机制
有没有更简洁(更依赖Rx)的方法
谢谢!您可以尝试:
var values = await _OnReaderEvent
.Where(r => r.PlaceId == placeId)
.Buffer(waitFor, 1)
.FirstAsync(); // get list of matching elements during waitFor time
return values.FirstOrDefault(); // return first element or null if the list is empty
为什么不使用简单的Rx版本的代码:
public async Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
return await
_OnReaderEvent
.Where(r => r.PlaceId == PlaceId)
.Buffer(waitFor, 1)
.Select(xs => xs.FirstOrDefault())
.FirstOrDefaultAsync()
.ToTask();
}
public异步任务WaitForReaderAsync(int-PlaceId,TimeSpan-waitFor)
{
返回等待
_OnReaderEvent
.Where(r=>r.PlaceId==PlaceId)
.Buffer(waitFor,1)
.Select(xs=>xs.FirstOrDefault())
.FirstOrDefaultAsync()
.ToTask();
}
这个问题可以用很多不同的方法来解决。这里有一种方法,利用,返回
,延迟
和第一异步
操作符:
public Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
return _OnReaderEvent
.Where(r => r.PlaceId == PlaceId)
.Amb(Observable.Return(default(ReaderEvent)).Delay(waitFor))
.FirstAsync()
.ToTask();
}
在本例中,看起来r是IList,但另一方面看起来很有希望:var values=wait _OnReaderEvent.FirstAsync(r=>r.PlaceId==PlaceId).Buffer(waitFor);return values.FirstOrDefault();Buffer()似乎仍在阻塞。如果行中没有事件,它不会终止:(Buffer return List,其中T是可观察项的类型。它是在waitFor的时间窗口期间生成的所有项的列表,每个waitFor都应该返回空列表。如果执行Buffer->FirstAsync,那么您将获得第一个列表。请参见。明白了。那么这就是我需要的:var values=wait\u OnReaderEvent.where(r=>r.PlaceId==PlaceId).Buffer(waitFor,1).FirstAsync();返回值.FirstOrDefault();您为我指出了正确的方向!几乎:)您错过了1 as Buffer参数,在第一个元素到达时立即返回该参数非常重要。遗憾的是,没有任何方法接受取消令牌。您可以使用取消令牌包装所有这些内容,并仅在取消时处理订阅。它不会以这种方式终止。但在添加.FirstOrDefaultAsync()之后在.ToTask()成功之前。是的,这是最干净的。我可以无缝地添加我的取消令牌。谢谢!@ZorgoZ-执行取消令牌代码没有用,因为你实际上没有用它取消底层的可观察对象。这有点毫无意义。
public Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
return _OnReaderEvent
.Where(r => r.PlaceId == PlaceId)
.FirstAsync()
.Timeout(waitFor, Observable.Return(default(ReaderEvent)))
.ToTask();
}