一律取消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;
我不确定在我的代码中对事件做出反应的两种可能性。大多数情况下,我关心哪个需要更少的资源。
我有一个方法,观察者注册到 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 aTaskCanceledException
?
是的。添加空延续:
var delayTask = Task.Delay(1000, cancellationToken);
var continuationTask = delayTask.ContinueWith(task => { });
await continuationTask;