Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm unsure about two possibilities to react to an event in my code. Mostly I'm concerned about which one needs less resources. I have a method with an observer registered to an eventproducer. If the eventproducer returns something, the method exits and the caller of the method starts the method again (you can think of it as a kind of a long polling).

The eventproducer sometimes fires lots of events per seconds and sometimes rests for minutes.

The first approach was to await a delay of 500ms and then check if there is something to return or otherwise (until a timeout of 5 minutes) again delay for 500ms.

eventProducer.RegisterListener((events)=>{evList.Add(events)});
while(evList.Count=0 && !TimeOut){
    await Task.Delay(500);}
eventProducer.UnRegister(...);
// return evList, method gets recalled immediately

The second approach was to use a CancellationToken. If the eventproducer produces something, the CancellationTokenSource cancels the source. And in the method I wait for 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

The advantages of the second approach are that the method immediately returns if the producer produces something and that we don't have to await and awake in a loop.

But with the second approach every time the producer produces something, a TaskCanceledException is thrown. I'm concerned that this could affect system load more than the awake and await every 500ms especially i times when the eventproducer produces lots of events.

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? I.E. something like task.setComplete?

like image 364
xartal Avatar asked Jun 09 '15 18:06

xartal


People also ask

How to handle Task cancelled exception in c#?

Just throw away catch (OperationCanceledException) block, and set proper continuations: var cts = new CancellationTokenSource(); var task = Task. Factory. StartNew(() => { var i = 0; try { while (true) { Thread.

How to Cancel async Task c#?

You can cancel an asynchronous operation after a period of time by using the CancellationTokenSource. CancelAfter method if you don't want to wait for the operation to finish.

What is the use of CancellationToken in c#?

A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects. You create a cancellation token by instantiating a CancellationTokenSource object, which manages cancellation tokens retrieved from its CancellationTokenSource. Token property.

What is task Cancelled exception?

It works fine when have one or two tasks however throws an error "A task was cancelled" when we have more than one task listed. List<Task> allTasks = new List<Task>(); allTasks.Add(....


1 Answers

If you want an immediate notification similar to what a cancellation gives you but without an exception you can simply use TaskCompletionSource.

TaskCompletionSource is how you create a promise task. You get an uncompleted task from the Task property and you complete it (or cancel) with SetResult. You can use it to actually pass on the result itself:

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

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

This solution doesn't have any exceptions and doesn't use unnecessary polling


To answer your specific questions:

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

Probably. You need to test and prove it's really an issue.

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

Yes. Add an empty continuation:

var delayTask = Task.Delay(1000, cancellationToken);
var continuationTask = delayTask.ContinueWith(task => { });
await continuationTask;
like image 124
i3arnon Avatar answered Sep 19 '22 13:09

i3arnon