一律取消task.delay还是用异常来控制流量?

Cancel task.delay without exception or use exception to control flow?

我不确定在我的代码中对事件做出反应的两种可能性。大多数情况下,我关心哪个需要更少的资源。 我有一个方法,观察者注册到 eventproducer。如果 eventproducer return 是什么东西,方法退出,方法的调用者再次启动方法(你可以把它想象成一种长轮询)。

eventproducer 有时每秒会触发很多事件,有时会休息几分钟。

第一种方法是等待 500 毫秒的延迟,然后检查是否有需要 return 的东西,否则(直到超时 5 分钟)再次延迟 500 毫秒。

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

第二种方法的优点是,如果生产者生产了一些东西,方法会立即 returns,我们不必在循环中等待和唤醒。

但是对于第二种方法,每当生产者生产一些东西时,都会抛出一个 TaskCanceledException。我担心这对系统负载的影响可能大于每 500 毫秒唤醒和等待一次,尤其是当 eventproducer 产生大量事件时。

我是否高估了抛出和捕获异常的成本?有没有办法用 CancellationToken 取消 Task.Delay 但不抛出 TaskCanceledException? IE。像 task.setComplete?

如果您想要一个类似于取消给您的即时通知,但无一例外,您可以简单地使用 TaskCompletionSource.

TaskCompletionSource 是创建承诺任务的方式。您从 Task 属性 得到一个未完成的任务,然后用 SetResult 完成(或取消)它。您可以使用它实际传递结果本身:

var tcs = new TaskCompletionSource<Events>();
eventProducer.RegisterListener(events => tcs.SetResult(events));

var result = await tcs.Task;
eventProducer.UnRegister(...);

这个解决方案没有任何例外,也没有使用不必要的轮询


回答您的具体问题:

Am I overestimating the cost of throwing and catching an exception?

可能吧。您需要测试并证明这确实是一个问题。

And is there a way to cancel Task.Delay with a CancellationToken but without throwing a TaskCanceledException?

是的。添加空延续:

var delayTask = Task.Delay(1000, cancellationToken);
var continuationTask = delayTask.ContinueWith(task => { });
await continuationTask;