I created a task like the following code
Task task = new(async () =>
{
// without await Task Delay dont work
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("Task is down");
});
task.Start();
var awaiter = task.GetAwaiter();
awaiter.OnCompleted(() =>
{
Console.WriteLine("Task is Completed");
});
Why the task completed first is printed and then the task is down
The task is complete and will be printed if my operation is not finished yet
Because Task constructors are not async aware. No overloads take an function returning a Task so the task created via constructor and async lambda will finish as soon as first await (of unfinished task) is encountered.
And in general you should try avoid using Task constructors and prefer Task.Run instead. From the docs:
This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.
Rather than calling this constructor, the most common way to instantiate a Task object and launch a task is by calling the static Task.Run(Action) or TaskFactory.StartNew(Action) method.
Related
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.
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.
Let's say I have two tasks, with the following requirements:
Both are asynchronous.
Both run in parallel
The moment one of them completes I need to know which one did.
I came up with the following code, but it just hangs after both tasks get started (the WaitAny function never returns). I am also getting a squiggly line under Run function telling me to add await inside it, but VS complains when I try to add it in front of Task.WaitAny. Should I be wrapping WaitAny in another Task? What am I doing wrong?
async void Run()
{
Task task1 = Task1();
Task task2 = Task2();
int completedTaskIdx = Task.WaitAny(task1, task2);
Debug.WriteLine("completedTaskIdx = {0}", completedTaskIdx.ToString());
}
async Task Task1()
{
Debug.WriteLine("Task 1 Start");
await Task.Delay(5000);
Debug.WriteLine("Task 1 Stop");
}
async Task Task2()
{
Debug.WriteLine("Task 2 Start");
await Task.Delay(10000);
Debug.WriteLine("Task 2 Stop");
}
Don't block the UI thread when using asnyc/await, you will cause dedlocks. Your WaitAny() causes you to get a deadlock. Use WhenAny instead, you can use Array.IndexOf( to translate the returned task back in to the index.
async Task Run()
{
Task task1 = Task1();
Task task2 = Task2();
var tasks = new[] {task1, task2};
Task completedTask = await Task.WhenAny(tasks);
//It is a good idea to await the retuned task, this is the point a execption would
//be raised if the task finished with a exception.
await completedTask;
int completedTaskIdx = Array.IndexOf(tasks, completedTask);
//.ToString() will cause you to have a bug, you are calling the
//wrong overload of WriteLine. The correct overload will call .ToString() for you.
Debug.WriteLine("completedTaskIdx = {0}", completedTaskIdx);
}
I also fixed a bug in your Debug.WriteLine( call, you where calling this overload when you wanted this overload. I also replaced your async void with async Task, you should never do asnyc void unless you are using it to match a event handler signature.
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.
I was just experimenting to see what happens when a cold task (i.e. a Task which hasn't been started) is awaited. To my surprise the code just hung forever and "Finsihed" is never printed. I would expect that an exception is thrown.
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
}
void Main()
{
Test1().Wait();
Console.WriteLine("Finished");
}
Then I though perhaps the task can be started from another thread, so I changed the code to:
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
Console.WriteLine("Test1 Finished");
}
void Main()
{
var task1 = Test1();
Task.Run(() =>
{
Task.Delay(5000);
task1.Start();
});
task1.Wait();
Console.WriteLine("Finished");
}
But it is still blocked at task1.Wait(). Does anyone know if there is way to start a cold task after it has being awaited?
Otherwise it seems there is no point in being able to await a cold task, so perhaps the task should either be started when awaited or an exception should be thrown.
Update
I was awaiting the wrong task, i.e. the outer task returned by Test1 rather than the one newed inside it. The InvalidOperationException mentioned by #Jon Skeet was being thrown inside Task.Run however because the resulting task was not observed, the exception was not thrown on the main thread. Putting a try/catch inside Task.Run or calling Wait() or Result on the task returned by Task.Run threw the exception on the main console thread.
You're trying to start the task returned by the async method - that isn't the cold task that you started out with. Indeed, if you add some diagnostics to your Task.Run call, you'll see that an exception is thrown:
System.InvalidOperationException: Start may not be called on a promise-style task.
Here's an example showing what I think you were really trying to do:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Test
{
static void Main(string[] args)
{
// Not using Task.Delay! That would be pointless
Task t1 = new Task(() => Thread.Sleep(1000));
Task t2 = Await(t1);
Console.WriteLine(t2.Status);
Console.WriteLine("Starting original task");
t1.Start();
Console.WriteLine(t2.Status);
t2.Wait();
Console.WriteLine(t2.Status);
}
static async Task Await(Task task)
{
Console.WriteLine("Beginning awaiting");
await task;
Console.WriteLine("Finished awaiting");
}
}
Note the use of Thread.Sleep instead of Task.Delay; unless you're using the result of Task.Delay, it basically does nothing. Using Thread.Sleep is emulating real work to be done in the other task.
As for why awaiting an unstarted task doesn't throw an exception - I think that's reasonable, to be honest. It allows for situations like the above to be valid, which may in some cases make life easier. (You may create a lot of tasks before starting them, for example - and you may want to start waiting for them to finish before you start them.)
Does anyone know if there is way to start a cold task after it has
being awaited?
You still can create a cold task from an async method and start it later, if that's what you want:
class Program
{
public static async Task Test1()
{
await Task.Delay(1000);
Console.WriteLine("Test1 is about to finish");
}
static void Main(string[] args)
{
var taskOuter = new Task<Task>(Test1);
var taskInner = taskOuter.Unwrap();
Task.Run(() =>
{
Thread.Sleep(2000);
// run synchronously
taskOuter.RunSynchronously();
// or schedule
// taskOuter.Start(TaskScheduler.Defaut);
});
taskInner.Wait();
Console.WriteLine("Enter to exit");
Console.ReadLine();
}
}