我不确定在代码中对事件作出反应的两种可能性。大多数情况下,我担心哪一个需要的资源更少。
我有一个方法,观察者注册了eventproducer
。如果eventproducer
返回某个内容,则该方法退出并且该方法的调用者再次启动该方法(您可以将其视为一种长轮询)。
eventproducer
有时会每秒触发大量事件,有时会持续几分钟。
第一种方法是等待延迟500ms,然后检查是否有东西要返回或以其他方式(直到5分钟超时)再次延迟500ms。
eventProducer.RegisterListener((events)=>{evList.Add(events)});
while(evList.Count=0 && !TimeOut){
await Task.Delay(500);}
eventProducer.UnRegister(...);
// return evList, method gets recalled immediately
第二种方法是使用CancellationToken
。如果eventproducer
产生某些内容,则CancellationTokenSource
会取消源代码。在方法中我等待Task.Delay(5min, cancellationToken)
。
eventProducer.RegisterListener((events)=>{evList.Add(events);
cancelSource.Cancel();}
try
{
await Task.Delay(5min, cancellationToken)
}
catch(TaskCanceledException){//nothing to do};
eventProducer.UnRegister(...);
// return evList, method gets recalled immediately
第二种方法的优点是,如果生产者生产某种东西并且我们不必等待并在循环中唤醒,该方法会立即返回。
但是,每当制作人产生某种东西时,使用第二种方法,就会抛出TaskCanceledException
。我担心这会影响系统负载而不是清醒,等待每500毫秒,尤其是当eventproducer
产生大量事件时。
我是否过高估计投掷和捕获例外的成本?是否可以使用Task.Delay
取消CancellationToken
,但不会抛出TaskCanceledException
? I.E.类似于task.setComplete
?
答案 0 :(得分:13)
如果您想要立即发送与取消相同的通知,但没有例外,您只需使用TaskCompletionSource
。
TaskCompletionSource
是您创建承诺任务的方式。您从Task
属性获得未完成的任务,并使用SetResult
完成(或取消)。您可以使用它来实际传递结果:
var tcs = new TaskCompletionSource<Events>();
eventProducer.RegisterListener(events => tcs.SetResult(events));
var result = await tcs.Task;
eventProducer.UnRegister(...);
此解决方案没有任何例外,也没有使用不必要的轮询
回答您的具体问题:
我是否高估了投掷和捕获例外的成本?
可能。你需要测试并证明它确实是一个问题。
有没有办法用
CancellationToken
取消Task.Delay,但没有抛出TaskCanceledException
?
是。添加一个空的延续:
var delayTask = Task.Delay(1000, cancellationToken);
var continuationTask = delayTask.ContinueWith(task => { });
await continuationTask;
答案 1 :(得分:-1)
您可以改用_cancellationToken.WaitHandle.WaitOne(timeout in milliseconds);
。它在提供的超时后返回,或者在触发取消令牌时立即返回,而不会抛出异常。