无异常地取消task.delay或使用异常来控制流

本文关键字:异常 控制流 delay 取消 task | 更新日期: 2023-09-27 17:54:49

我不确定对代码中的事件作出反应的两种可能性。我最关心的是哪一个需要更少的资源。我有一个注册到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。我担心这会比每500ms唤醒和等待对系统负载的影响更大,特别是当eventproducer产生大量事件时。

我是否高估了抛出和捕获异常的成本?有没有一种方法来取消Task.DelayCancellationToken,但不抛出TaskCanceledException ?比如task.setComplete ?

无异常地取消task.delay或使用异常来控制流

如果你想要一个类似于取消的即时通知,但没有异常,你可以简单地使用TaskCompletionSource

TaskCompletionSource是如何创建承诺任务。您从Task属性获得未完成的任务,并使用SetResult完成(或取消)它。你可以用它来传递结果本身:

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

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


回答您的具体问题:

我是否高估了抛出和捕获异常的成本?

。你需要测试并证明这确实是个问题。

是否有办法取消任务。有CancellationToken但不抛出TaskCanceledException的延迟?

是的。添加一个空的延续符:

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