Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TaskCreationOptions for an async Task Method

I have a method returning an async Task. I wish to call this but I'd like to customise the TaskCreationOptions sent to it, I'm trying to work out best practise for this, or how I should modify my approach?

Sample..

void Main(){        
    _runTask=Test(); // I wish to provide creation options here, ie LongRunning, and possibly also a cancellation token
}
async Task Test()
{
    await Task.Delay(10);
}
like image 825
user2849000 Avatar asked Dec 11 '22 07:12

user2849000


2 Answers

I wish to provide creation options here, ie LongRunning, and possibly also a cancellation token

You can't customize TaskCreationOptions on the Task object returned by an async method. Essentially, it doesn't make sense: you don't create the initial Task here, Task.Delay does. You can think of it like this:

Task Test()
{
    var scheduler = SynchronizationContext.Current != null ?
        TaskScheduler.FromCurrentSynchronizationContext() :
        TaskScheduler.Current;

    return Task.Delay(10).ContinueWith((t) => { }, 
        CancellationToken.None,
        TaskContinuationOptions.None, scheduler);
}

Now, that's possible to control TaskContinuationOptions for the task returned by ContinueWith (including TaskContinuationOptions.LongRunning), but not TaskCreationOptions.

You can however provide a cancellation token:

async Task Test(CancellationToken token)
{
    await Task.Delay(10, token);
    // ...
    token.ThrowIfCancellationRequested();
}
like image 92
noseratio Avatar answered Mar 09 '23 00:03

noseratio


You can't, and you don't need to. Neither of those options do what you think they do.

LongRunning is useless for async workloads; it would (in the current implementation) spin up a separate dedicated thread just to execute the first part of the async method (until the first await of an incomplete operation), and then resume the async method as regular work queued to the thread pool, ignoring that dedicated thread for the rest of the method.

Passing a CancellationToken to the task constructor is unnecessary; the only benefit it would get you in async code is that the code wouldn't start if the token is already canceled. And you can do the same thing by just checking it yourself before calling the async method. The proper way to handle cancellation with async methods is described in the Task-based Asynchronous Pattern documentation:

async Task TestAsync(CancellationToken cancellationToken)
{
  await Task.Delay(10, cancellationToken);
}
like image 29
Stephen Cleary Avatar answered Mar 09 '23 01:03

Stephen Cleary