取消task.delay无例外或使用异常来控制流量?

时间:2015-06-09 18:17:09

标签: c# asynchronous exception-handling async-await task-parallel-library

我不确定在代码中对事件作出反应的两种可能性。大多数情况下,我担心哪一个需要的资源更少。 我有一个方法,观察者注册了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

2 个答案:

答案 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);。它在提供的超时后返回,或者在触发取消令牌时立即返回,而不会抛出异常。