Why use async await if same can be achieved by Task.run - c#

Do both methods below behave the same way? Is there an internal difference as to how they work? Both methods will release the UI thread and continue executing once the delay is over.
public async Task DoSomethingAsync()
{
Console.WriteLine("Test Async start");
//Any long running operation
await Task.Delay(5000);
Console.WriteLine("Test Async end");
}
public void TestTask()
{
Console.WriteLine("Test Task start");
Task.Run(() =>
{
//Any long running operation
Thread.Sleep(10000);
}
).ContinueWith((prevTask) =>
{
Console.WriteLine("Test Task end");
});
}
**output**:
Test Async start
Test Task start
Test Async end
Test Task end

Asynchronous programming with async and await
The async and await keywords don't cause additional threads to be
created. Async methods don't require multithreading because an async
method doesn't run on its own thread. The method runs on the current
synchronization context and uses time on the thread only when the
method is active.
You don't need System.Threading for Tasks. The code below does because of CancellationToken.
One difference is with Task you have access to properties on the main (UI) thread.
See Quantity. You can even write to the main thread but probably not a good practice.
async Task<int> TaskDelayAsync(CancellationToken ct, IProgress<int> progress)
{
Debug.WriteLine(Quantity);
int i = 0;
while (true)
{
i++;
//Debug.WriteLine(i);
progress.Report(i);
ct.ThrowIfCancellationRequested();
await Task.Delay(500);
}
return i;
}

Related

This async method lacks 'await' operators and will run synchronously

my program has 3 warnings of the following statement:
This async method lacks 'await' operators and will run synchronously.
Consider using the 'await' operator to await non-blocking API calls,
or 'await Task.Run(...)' to do CPU-bound work on a background thread.
What is the warning try to tell me? What should I do?
This is my code: Is it running using multi-threading?
static void Main(string[] args)
{
Task task1 = new Task(Work1);
Task task2 = new Task(Work2);
Task task3 = new Task(Work3);
task1.Start();
task2.Start();
task3.Start();
Console.ReadKey();
}
static async void Work1()
{
Console.WriteLine("10 started");
Thread.Sleep(10000);
Console.WriteLine("10 completed");
}
static async void Work2()
{
Console.WriteLine("3 started");
Thread.Sleep(3000);
Console.WriteLine("3 completed");
}
static async void Work3()
{
Console.WriteLine("5 started");
Thread.Sleep(5000);
Console.WriteLine("5 completed");
}
The async keyword, by itself, doesn't really do much. Remove it from your code and your code will act exactly the same.
What does async do?
It changes what's valid inside of the method, specifically it allows you to use the await keyword
In turn, it means that the body of the method will be transformed, based on the awaits that are present in the body of the method.
And if the method returns a value, the method is also transformed to wrap the return value in a Task.
However, if you a) Don't have any awaits in your method body and b) are void returning, then nothing special will be achieved. The compiler warning does try to be clear about this - an async method without any awaits just plain doesn't make sense. awaits are the more important part of this feature.
If you are overriding an async method with a sync method you can:
await Task.Run(() => [YOUR SYNC METHOD]);
You have used 'async' keyword with method which indicates that Work1(),Work2() and Work3() methods are executed asynchronously,but you have not used 'await' keyword.So it executed as synchronously.Use 'await' keyword if you want to execute it asynchronously.
static async void Work1()
{
Console.WriteLine("10 started");
await Task.Delay(10000);
Console.WriteLine("10 completed");
}
static async void Work2()
{
Console.WriteLine("3 started");
await Task.Delay(3000);
Console.WriteLine("3 completed");
}
static async void Work3()
{
Console.WriteLine("5 started");
await Task.Delay(5000);
Console.WriteLine("5 completed");
}
Yes, your code will probably use multi-threading. However, it would still do so if you just removed the async keyword. As you did not explain why it's there, I suggest removing it.
If you want an async/await pattern, you can use Task.Delay() but I would suggest you read more on async/await before using it:
static async void Work3()
{
Console.WriteLine("5 started");
await Task.Delay(5000);
Console.WriteLine("5 completed");
}
You designated your methods (Work1, Work2, Work3) with the keyword async but none of your code within those methods uses the await operator to invoke asynchronous calls.

How to wait for later started task

In my code sample main thread doesn't wait when task2 is finished.
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
Console.WriteLine("Main 2");
var task2 = getTask2();
await Task.Delay(1);
Console.WriteLine("Main 3");
task2.Start();
await task2;
Console.WriteLine("Main end");
}
private async Task getTask1()
{
Console.WriteLine("task1 start");
await Task.Delay(100);
Console.WriteLine("task1 end");
}
private Task getTask2()
{
return new Task(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
Result of execution this code is
Main start
task1 start
task1 end
Main 2
Main 3
task2 start
Main end
task2 end
How can I change code where 'Main end' will be at the end of list.
In getTask2 you're starting a Task that just starts another Task. So when you await that Task it will continue executing as soon as it has finished starting the inner task, not when that inner task has finished.
In this case there's no reason at all for you to create a new task just to start a new task in the first place; you should just use the approach used in the first method.
Of course, if, for some reason, you do need to create a new Task just to start a new task then you need to get the inner Task out and ensure that the main method only continues executing when that inner Task has finished. There is an Unwrap method to create a Task that is logically equivalent to a Task that is the Result of another Task:
private Task getTask2()
{
Task<Task> task = new Task<Task>(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
task.Start();
return task.Unwrap();
}
But you don't even need to do that. Task.Run will automatically unwrap the value for you if it is given a lambda that produces a Task:
private Task getTask2()
{
return Task.Run(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
And once again, this is all assuming that just starting the asynchronous operation needs to be offloaded to another thread, which should be very rare. Usually this means that there's a bug in that asynchronous method in that it shouldn't be doing long running synchronous work before giving you its Task in the first place, but on occasion that method will be from an external library that you don't control, so this would be your only option.
The constructor overload for Task that you are using has the following signature:
public Task(Action action)
Although you can assign the following expression to a variable of type Action:
async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
}
this action will point to a synchronous function that will synchronously finish once the await in await Task.Delay(100); is reached (although the rest of the "action" will continue asynchronously). So, as far as the Task object is concerned, the execution of this Action is completed once this await is reached. This means that the await in await task2; will asynchronously continue at that point which explains why "Main end" is printed before "task2 end".
So basically, the constructor for Task does not support asynchronous actions, just synchronous ones.
To fix this problem, you need to start a task using a method that understands asynchronous actions. An asynchronous action looks like this: Func<Task>.
The Task.Run method supports asynchronous actions because it has the following overload:
public static Task Run(Func<Task> function)
So to fix your code, change return new Task(... to return Task.Run(... and then don't call the Start method on the returned task since Task.Run will create a Task that is already started.
If you want to delay the execution of the second "task", then I suggest to make the getTask2 method return Func<Task> (an asynchronous action) instead of Task like this:
private Func<Task> getTask2()
{
return async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
};
}
And then run such asynchronous action when you want from the Run method like this:
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
Console.WriteLine("Main 2");
var task2 = getTask2(); //construct asynchronous action
Console.WriteLine("Main 3");
await Task.Run(task2); //execute the asynchronous action
Console.WriteLine("Main end");
}
new Task( does not work with async functions by itself. You are creating a Task<Task>, you need to call Unwrap() before you await the inner task.
private Task<Task> getTask2()
{
return new Task(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
var task2 = getTask2();
Console.WriteLine("Main 3");
task2.Start();
await task2.Unwrap();
You have an error in your code. You have not marked the getTask2 method with the keyword async yet you await its result. Even though you are using an async delegate within the method you must use the async keyword if it is to be successfully awaited.
Here's an idea as to how you can accomplish your desired result.
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
await getTask2();
Console.WriteLine("Main end");
}
public async Task AlternateRun()
{
Console.WriteLine("Main start");
List<Task> task_list = new List<Task>();
task_list.Add(getTask1());
task_list.Add(getTask2());
var task_result = Task.WhenAll(task_list);
task_result.Wait();
Console.WriteLine("Main end");
}
private async Task getTask1()
{
Console.WriteLine("task1 start");
await Task.Delay(100);
Console.WriteLine("task1 end");
}
private async Task getTask2()
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
}
I have also added an AlternateRun method that will demonstrate an alternative style of awaiting multiple tasks. If you need your tasks to be completed in order then consider using the ContinueWith extension method.
Notice that I also removed your task2.Start()statement. This is already implied by the await keyword.
Hope this helps.

Execute list of async tasks

I have a list of async functions I want to execute in order. When I run the following code I get the output:
Task 1 before
Task 2 before
Finished tasks
Why are my async functions not being awaited correctly?
[Test]
public async Task AsyncTaskList()
{
var data = "I'm data";
var tasks = new List<Func<object, Task>>() {Task1, Task2};
tasks.ForEach(async task =>
{
await task(data);
});
Debug.WriteLine("Finished tasks");
}
private static async Task Task1(object data)
{
Debug.WriteLine("Task 1 before");
await Task.Delay(1000);
Debug.WriteLine("Task 1 after");
}
private static async Task Task2(object data)
{
Debug.WriteLine("Task 2 before");
await Task.Delay(1000);
Debug.WriteLine("Task 2 after");
}
Because the await inside your ForEach delegate actually completes after the method exits. Change it to an actual foreach loop and awaiting will work as expected.
ForEach has no specific handling for Func<Task> (few delegate-accepting methods in the Base Class Library do, and you should note that they will almost invariably return a Task themselves). ForEach will only run the synchronous portion of your lambda - and that is the portion preceding the first await of a Task which does not complete synchronously (which is Task.Delay in your case). This is why you're seeing the "before" messages popping up at the expected time. As soon as your delegate hits await Task.Delay, the the rest of your lambda is scheduled to run sometime in the future and ForEach moves on to the next item in the list. The scheduled task continuations will then run unobserved and complete later.

How to call async methods

Imagine there is a method
async static System.Threading.Tasks.Task Do()
{
//do sth
await Task.Delay(10000);
}
now , when I call the method with
Do();
Does it make a new thread ? or I have to create a thread as below
Task.Factory.StartNew(() => Do());
Do doesn't create a thread.
The part before the await runs on the calling thread and there's no thread at all during the delay. Task.Delay uses a timer internally which doesn't hold up a thread throughout the operation.
When the delay completes a thread from the ThreadPool is used to continue executing the method.
About Task.Factory.StartNew, why would you want to create a thread? If you need to offload a CPU intensive method to the thread pool then it's fine (although Task.Run is preferable) but if you don't then simply call Do and await the result:
await Do();
If you insist on creating a new thread for Do you need to use TaskCreationOptions.LongRunning:
Task.Factory.StartNew(() => Do(), TaskCreationOptions.LongRunning);
But since Do is an async method that releases the thread when it reaches the first await this doesn't make any sense as I've explained on my blog: LongRunning Is Useless For Task.Run With async-await
All my case is this :
public void Start(Action action)
{
Token = CancellationToken.None;
IsRunning = true;
Task.Factory.StartNew(() => Do(action), TaskCreationOptions.LongRunning);
}
async Task Do(Action action)
{
while (IsRunning)
{
action();
await Task.Delay(Interval, Token);
}
}
then I call Start() with five different actions with different intervals.
Is this Correct to use ?

The lack of non-capturing Task.Yield forces me to use Task.Run, why follow that?

Apologies in advance if this question is opinion-based. The lack of Task.Yield version which wouldn't capture the execution context was already discussed here. Apparently, this feature was present in some form in early versions of Async CTP but was removed because it could easily be misused.
IMO, such feature could be as easily misused as Task.Run itself. Here's what I mean. Imagine there's an awaitable SwitchContext.Yield API which schedules the continuation on ThreadPool, so the execution will always continues on a thread different from the calling thread. I could have used it in the following code, which starts some CPU-bound work from a UI thread. I would consider it a convenient way of continuing the CPU-bound work on a pool thread:
class Worker
{
static void Log(string format, params object[] args)
{
Debug.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, String.Format(format, args));
}
public async Task UIAction()
{
// UI Thread
Log("UIAction");
// start the CPU-bound work
var cts = new CancellationTokenSource(5000);
var workTask = DoWorkAsync(cts.Token);
// possibly await for some IO-bound work
await Task.Delay(1000);
Log("after Task.Delay");
// finally, get the result of the CPU-bound work
int c = await workTask;
Log("Result: {0}", c);
}
async Task<int> DoWorkAsync(CancellationToken ct)
{
// start on the UI thread
Log("DoWorkAsync");
// switch to a pool thread and yield back to the UI thread
await SwitchContext.Yield();
Log("after SwitchContext.Yield");
// continue on a pool thread
int c = 0;
while (!ct.IsCancellationRequested)
{
// do some CPU-bound work on a pool thread: counting cycles :)
c++;
// and use async/await too
await Task.Delay(50);
}
return c;
}
}
Now, without SwitchContext.Yield, DoWorkAsync would look like below. It adds some extra level of complexity in form of async delegate and task nesting:
async Task<int> DoWorkAsync(CancellationToken ct)
{
// start on the UI thread
Log("DoWorkAsync");
// Have to use async delegate
// Task.Run uwraps the inner Task<int> task
return await Task.Run(async () =>
{
// continue on a pool thread
Log("after Task.Yield");
int c = 0;
while (!ct.IsCancellationRequested)
{
// do some CPU-bound work on a pool thread: counting cycles :)
c++;
// and use async/await too
await Task.Delay(50);
}
return c;
});
}
That said, implementing SwitchContext.Yield may actually be quite simple and (I dare to say) efficient:
public static class SwitchContext
{
public static Awaiter Yield() { return new Awaiter(); }
public struct Awaiter : System.Runtime.CompilerServices.INotifyCompletion
{
public Awaiter GetAwaiter() { return this; }
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem((state) => ((Action)state)(), continuation);
}
public void GetResult() { }
}
}
So, my question is, why should I prefer the second version of DoWorkAsync over the first one, and why would using SwitchContext.Yield be considered a bad practice?
You don't have to put the Task.Run in DoWorkAsync. Consider this option:
public async Task UIAction()
{
// UI Thread
Log("UIAction");
// start the CPU-bound work
var cts = new CancellationTokenSource(5000);
var workTask = Task.Run(() => DoWorkAsync(cts.Token));
// possibly await for some IO-bound work
await Task.Delay(1000);
Log("after Task.Delay");
// finally, get the result of the CPU-bound work
int c = await workTask;
Log("Result: {0}", c);
}
This results in code with much clearer intent. DoWorkAsync is a naturally synchronous method, so it has a synchronous signature. DoWorkAsync neither knows nor cares about the UI. The UIAction, which does care about the UI thread, pushes off the work onto a background thread using Task.Run.
As a general rule, try to "push" any Task.Run calls up out of your library methods as much as possible.

Categories