Creating/initializing task without starting it immediately - c#

I want to create/initialize a Task object from a async Task method without starting it directly. When searching online I only find answer with Task created from void methods.
This is the task method I want to execute. As I need to do some web requests async, the method needs to be async. I want to do exception handling as well, so it can't be an async void.
private async Task WebRequestTask()
{
try
{
string ResponseText = await httpClient.GetStringAsync("https://fakeurl.com");
// process response code
}
catch (Exception ex)
{
// handle error
}
}
This is my main method where I want to create the task:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Task RequestTask = ... // here I want to initialize the task without starting
// chain continuation tasks to RequestTask
RequestTask.Start();
}
I've tried following solutions but nothing answers my need:
Solution 1
Task RequestTask = new Task(WebRequestTask);
Task RequestTask = WebRequestTask;
-> both cause a compiler error
Solution 2
Task RequestTask = Task.Run(WebRequestTask);
-> this start the task async and the current method continues (but here it could be possible an exception is thrown before the continuation tasks are chained)
Solution 3
Task RequestTask = WebRequestTask();
-> this start the task synchronously and chaining happens after the task is finished
Solutions 4
Task<Task> OuterTask = new Task<Task>(LoginToAzure);
await LoginAzureTask.Unwrap();
-> this start the outer task but the inner task is never called
How can I attach this Task method to an Task object, so that I can first attach continuation tasks/set some options and then start it? When it's possible, I'd like to use the cancellation token as well.

I want to create/initialize a Task object from a async Task method without starting it directly.
The best way to do this is to use delegates. E.g.:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Func<Task> taskFactory = () => WebRequestTask(TokenSource);
// chain continuation tasks
Task task = taskFactory(); // Start the task
... // await the task or save it somewhere
}
Side note: I strongly recommend implementing "chain continuation tasks" using await rather than ContinueWith. await is a clean and safe approach; ContinueWith is a low-level, dangerous method.

I want to do exception handling as well, so it can't be an async void.
Since you are handling the error inside WebRequestTask() that doesn't really matter.
The preferred option would be to make StartTask an async Task but if that isn't possible, make it async void. That takes care of the "without starting" requirement too.
So, keep it simple:
private async void StartTask()
{
TokenSource = new CancellationTokenSource();
try
{
await WebRequestTask(TokenSource.Token);
// here I want to initialize the task without starting -- irrelevant in an async void
await otherTask(); // continuation(s)
}
catch()
{
// handle residual errors
}
}

The asynchronous methods that are implemented with the async keyword are creating "hot" Tasks, in other words Tasks that are already started. You can defer the execution of an async method by wrapping it in another Task, thus creating a nested Task<Task>, like this:
{
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
Task<Task> taskTask = new Task<Task>(() => WebRequestAsync(token));
Task task = taskTask.Unwrap(); // The task is not started yet
// Chain continuations to task
taskTask.RunSynchronously(TaskScheduler.Default);
// The task is now started
}
private async Task WebRequestAsync(CancellationToken cancellationToken)
{
try
{
string responseText = await httpClient.GetStringAsync(
"https://fakeurl.com", cancellationToken);
// Process response code
}
catch (Exception ex)
{
// Handle error
}
}
The taskTask variable is the wrapper that represents just the execution of the WebRequestAsync method, not the completion of the Task that this method creates. The wrapper task is completed immediately after calling the RunSynchronously method, and after that point your code will have no use for it. The task variable "unwraps" the wrapper, and represents the actual asynchronous web request. It is completed when the asynchronous operation completes.
As you can see this technique is rather cumbersome. You'll rarely see it in application code, because pure async-await composition is much more convenient to use.

Related

SignalR is not Async?

While messing around with SignalR I found a behaviour that confuse me.
Calling StartCountDown from a client then make a call to Join behaves like
wait 10 seconds
Call clients CountDownStarted
Then call PlayerJoined
What I expected.
Call start CountDown, return
immediately call PlayerJoined
After 10 seconds complete CountDownStarted.
public class AHub : Hub
{
public async Task Join(string player)
{
await Clients.All.PlayerJoined(player);
}
public async Task StartCountDown()
{
await Task.Delay(10000);
await Clients.All.CountDownStarted();
}
}
This is from a SignalR Hub
This is a common misconception about the async and await pattern. Awaiting something does actually await the completion of the task.
If you want to run the task unobserved (or colloquially known as fire and forget), you could do thus
// task gets started hot and unobserved, remove the warning with a discard
_ = StartCountDownAsync();
Note : An exception that's raised in a method that returns a Task or Task<TResult> is stored in the returned task. If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown.
As a best practice, you should always await the call.
Though, you have other options. Which is to start a task, complete other tasks, and then await the completion of the original
Given
public async Task SomeTask1() { }
public async Task SomeTask2() { }
public async Task SlowApiAsync() { }
You might want
var slowApiTask = SlowApiAsync();
await SomeTask1();
await SomeTask2();
await slowApiTask;
Or if you want to run all the tasks concurrently (and yet await them all)
var slowApiTask = SlowApiAsync();
var task1 = SomeTask1();
var task2 = SomeTask2();
await Task.WhenAll(slowApiTask,task1,task2)

Async method blocking on unawaited task

In my current project, I have a piece of code that, after simplifying it down to where I'm having issues, looks something like this:
private async Task RunAsync(CancellationToken cancel)
{
bool finished = false;
while (!cancel.IsCancellationRequested && !finished)
finished = await FakeTask();
}
private Task<bool> FakeTask()
{
return Task.FromResult(false);
}
If I use this code without awaiting, I end up blocking anyway:
// example 1
var task = RunAsync(cancel); // Code blocks here...
... // Other code that could run while RunAsync is doing its thing, but is forced to wait
await task;
// example 2
var task = RunAsync(cancelSource.Token); // Code blocks here...
cancelSource.Cancel(); // Never called
In the actual project, I'm not actually using FakeTask, and there usually will be some Task.Delay I'm awaiting in there, so the code most of the time doesn't actually block, or only for a limited amount of iterations.
In unit testing, however, I'm using a mock object that does pretty much do what FakeTask does, so when I want to see if RunAsync responds to its CancellationToken getting cancelled the way I expect it to, I'm stuck.
I have found I can fix this issue by adding for example await Task.Delay(1) at the top of RunAsync, to force it to truly run asynchronous, but this feels a bit hacky. Are there better alternatives?
You have an incorrect mental picture of what await does. The meaning of await is:
Check to see if the awaitable object is complete. If it is, fetch its result and continue executing the coroutine.
If it is not complete, sign up the remainder of the current method as the continuation of the awaitable and suspend the coroutine by returning control to the caller. (Note that this makes it a semicoroutine.)
In your program, the "fake" awaitable is always complete, so there is never a suspension of the coroutine.
Are there better alternatives?
If your control flow logic requires you to suspend the coroutine then use Task.Yield.
Task.FromResult actually runs synchronously, as would await Task.Delay(0). If you want to actually simulate asynchronous code, call Task.Yield(). That creates an awaitable task that asynchronously yields back to the current context when awaited.
As #SLaks said, your code will run synchronously. One thing is running async code, and another thing is running parallel code.
If you need to run your code in parallel you can use Task.Run.
class Program
{
static async Task Main(string[] args)
{
var tcs = new CancellationTokenSource();
var task = Task.Run(() => RunAsync("1", tcs.Token));
var task2 = Task.Run(() => RunAsync("2", tcs.Token));
await Task.Delay(1000);
tcs.Cancel();
Console.ReadLine();
}
private static async Task RunAsync(string source, CancellationToken cancel)
{
bool finished = false;
while (!cancel.IsCancellationRequested && !finished)
finished = await FakeTask(source);
}
private static Task<bool> FakeTask(string source)
{
Console.WriteLine(source);
return Task.FromResult(false);
}
}
C#'s async methods execute synchronously up to the point where they have to wait for a result.
In your example there is no such point where the method has to wait for a result, so the loop keeps running forever and thereby blocking the caller.
Inserting an await Task.Yield() to simulate some real async work should help.

Async/await in repository

How can I make async/await method in repository? Should I use Task.Run?
public virtual void Add(T entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
_context.Entry(entity);
Entities.Add(entity);
}
catch (DbEntityValidationException dbEx)
{
...
}
}
Using async/await is only useful if your function is async and you expect that the clients that call your functions are async (and their clients etc).
The reason is, because calling an async function does not mean that the function is performed. It means that a Task is scheduled to run the function performed by a thread in a pool of available threads. After the task is scheduled your function can continue with the next statement. When it needs the result of the scheduled task it awaits until the scheduled task is finished.
The advantage above starting a thread yourself is that this saves the overhead to start a new thread and do the cleanup afterwards. The disadvantage is that you are not certain that a thread is available the moment you schedule the task.
If your function is not declared async you still can schedule a task using Task.Run( () => OtherFunction(...)), but you can't await for it. To wait for the task to finish you have to call Task.Wait(...). In the meantime the thread that called your function can't continue. If this thread is the UI thread you'll notice this because your UI is not responsive.
So if you want to make proper use of other async functions, its best to declare your function async and return Task instead of void and Task<TResult> instead of TResult. Call the other async function, do other things and await the task before returning. The clients need to be async and return Task / Task<TResult>. The only async client that may return void is the event handler.
Example:
public async void button1_clicked(object sender, ...)
{
Task<int> task1 = this.DoSomethingAsync(...);
// while task1 is running you can do other things
// you can even schedule another task:
Task task2 = this.DoSomethingElseAsync(...);
// do other things. After a while you need the result of task1:
int task1Result = await task1;
// or if you want to await until both tasks are finished:
await Task.WhenAll(new Task[]{task1, task2});
int task1Result = task1.Result;
}
private async Task<int> DoSomethingAsync(...)
{
// schedule another async task and await:
await DoSomethingElseAsync(...);
return 42;
}
private async Task DoSomethingElseAsync(...)
{
// do something really important:
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
}
If you don't want to force your clients to be async, consider creating two versions, one async and one non-async. This is quite common. See definitions in Files, Streams, DbConnections etc.

How to safely cancel a task using a CancellationToken and await Task.WhenAll

I have a framework which creates a CancellationTokenSource, configures CancelAfter, then calls an async method and passes the Token. The async method then spawns many tasks, passing the cancellation token to each of them, and then awaits the collection of tasks. These tasks each contain logic to gracefully cancel by polling IsCancellationRequested.
My issue is that if I pass the CancellationToken into Task.Run() an AggregateException is thrown containing a TaskCanceledException. This prevents the tasks from gracefully canceling.
To get around this I can not pass the CancelationToken into Task.Run, however I'm not sure what I will be losing. For instance, I like the idea that if my task hangs and cannot perform the graceful cancel this exception will force it down. I was thinking I could string along two CancelationTokens to handle this, one 'graceful' and the other 'force'. However, I don't like that solution.
Here is some psudo-code representing what I described above..
public async Task Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(30000);
await this.Run(cts.Token);
}
public async Task Run(CancellationToken cancelationToken)
{
HashSet<Task> tasks = new HashSet<Task>();
foreach (var work in this.GetWorkNotPictured)
{
// Here is where I could pass the Token,
// however If I do I cannot cancel gracefully
// My dilemma here is by not passing I lose the ability to force
// down the thread (via exception) if
// it's hung for whatever reason
tasks.Add(Task.Run(() => this.DoWork(work, cancelationToken))
}
await Task.WhenAll(tasks);
// Clean up regardless of if we canceled
this.CleanUpAfterWork();
// It is now safe to throw as we have gracefully canceled
cancelationToken.ThrowIfCancellationRequested();
}
public static void DoWork(work, cancelationToken)
{
while (work.IsWorking)
{
if (cancelationToken.IsCancellationRequested)
return // cancel gracefully
work.DoNextWork();
}
}
I recommend that you follow the standard cancellation pattern of throwing an exception rather than just returning:
public static void DoWork(work, cancellationToken)
{
while (work.IsWorking)
{
cancellationToken.ThrowIfCancellationRequested();
work.DoNextWork();
}
}
If you have cleanup work to do, that's what finally is for (or using, if you can refactor that way):
public async Task Run(CancellationToken cancellationToken)
{
HashSet<Task> tasks = new HashSet<Task>();
foreach (var work in this.GetWorkNotPictured)
{
tasks.Add(Task.Run(() => this.DoWork(work, cancellationToken))
}
try
{
await Task.WhenAll(tasks);
}
finally
{
this.CleanUpAfterWork();
}
}
Provide the CancellationToken to Task.Run in addition to passing it to the method doing the work. When you do this Task.Run can see that the exception thrown was caused by the CancellationToken it was given, and will mark the Task as cancelled.
tasks.Add(Task.Run(() => this.DoWork(work, cancelationToken),
cancelationToken));
Once you've done this you can ensure that DoWork throws when the token is cancelled, rather than checking IsCancellationRequested to try to end by being marked as "completed successfully".

Can I not await for async Task without making it async void?

When using async Task method it is required to place await before method. I need code to be executed in non UI blocking manner and don't want to await. My only idea is to use:
private void TaskFactory()
{
CancellationTokenSource token_TaskFactory = new CancellationTokenSource();
ParallelOptions parOpts = new ParallelOptions();
parOpts.CancellationToken = token_TaskFactory.Token;
parOpts.MaxDegreeOfParallelism = Environment.ProcessorCount;
TaskCreationOptions atp = new TaskCreationOptions();
atp = TaskCreationOptions.PreferFairness;
Task TaskFactory = Task.Factory.StartNew(() =>
{
if (!token_TaskFactory.IsCancellationRequested)
{
Thread.Sleep(5000);
}
else
{
}
}, token_TaskFactory.Token, atp, TaskScheduler.Default);
}
When using async Task method it is required to place await before method.
The correct way to handle this is to await the method, and make the calling method async Task or async Task<TResult>. This will have a cascading effect as async travels up through your code.
In a UI application, you will usually end up at an event handler, which cannot be async Task, and at that point you can make the event handler async void.
If you cheat this process by making a regular method async void, you cause other problems, particularly around error handling and composability (i.e. unit testing). See my MSDN article or one of the many talks about why async void should be avoided.
I need code to be executed in non UI blocking manner and don't want to await.
Why don't you "want" to use await? You have asynchronous code that you need to run in a non-blocking fashion, and that's exactly what await does!

Categories

Resources