Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.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# 接收:等待第一项一段时间_C#_Task Parallel Library_System.reactive - Fatal编程技术网

C# 接收:等待第一项一段时间

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

我想将我遗留的基于事件的方法转换为基于可观察的方法,但我对Rx非常陌生,所以我现在被卡住了

我有一个事件源,它现在是可观察的。在某个时间点,我必须启动一个方法,该方法要么返回行中的下一个元素,要么在超时时返回null

基于事件的方法如下所示:

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();
}