-
- 2.1. Defining Asynchronous methods: The problem
- 2.2. Defining Asynchronous methods: The solution
- 2.3. Defining Asynchronous methods that return values
- 2.4. Asynchronous methods gotchas
- 2.5. Tasks, memory allocation and efficiency
Two reasons:
- To Improve responsiveness: Do many things simultanously.
Tasks
achieve this, performing operations in parallel and improving throughput in compute-bound applications. - To Improve scalability: Decompose a process to be performed in parallel and run concurrently.
Asynchronous operations
achieve this, leaving the user interface thread free to handle user interactions.- Responsiveness is important! UI operates by using a single thread of execution. User expects an application to respond when they interact with the UI, even if the application is currently performing a large and complex calculation.
- Blocking user interaction while waiting for an
- An
Asynchronous method
:- it does not block the current thread on which it starts to run.
- when called:
- return control to the calling environment
- it uses a background thread, enabling the caller to continue running on the current thread.
- It uses:
async
method modifier:await
operator: it releases the current thread and wait for a task to complete in the background. When the task finishes, control return to the method which continues the next statement.
- If a very time consuming process is invoked from the UI (i.e., by clicking something), the UI will become unresponsive until this method completes.
- In the example below:
- To change the
message.Text
, the software shall complete the three methods before.
- To change the
private verySlowMethod()
{
fistLongOperation();
secondLongOperation();
thirdLongOperation();
message.Text = "Process completed" // UI - command
}
- Use instead:
async
modifier: indicated that a method continas functionality that can be run asynchronously.await
operator: specifies the points at which this asynchronouly functionality should be performed.
- The solution to the method in the previous section is presented below:
private async verySlowMethod()
{
await method1();
await method2();
await method3();
message.Text = "Process completed" // UI - command
}
//long operations
private void method1()
{
//...
}
private void method2()
{
//...
}
private void method3()
{
//...
}
This code is pretty similar to the original version.
- When the
C#
compiler encounters theawait
operator in anasync
method, it reformats the operand that follows this operator as a tasks that runs on the same thread as theasync
method.- The remainder of the code is converted into a continuation that runs when the task completes, again running on the same thread!!!.
await
operator can only be used insideasync
methods.
- The structure for an
await
statement is:await awaitableObject;
- where
awaitableObject
shall be aTask
or a method thatreturn
it. So the methods shall be modified as presented below:
private Task method1()
{
Task t = Task.Run(() => {/* code from original version of the method*/});
return t
}
- In case that the
method
can be decomposed into more than oneTasks
, it shall be modified as follows:
private async Task method1()
{
Task t1 = Task.Run(() => {/* first part of the code from original version of the method*/});
Task t2 = Task.Run(() => {/* second part of the code from original version of the method*/});
await t1;
await t2;
// No return statement is required!
}
- NOTE1: No
return
statements is required in theasync Task
method. - NOTE2: If you don't include an
await
statement in anasync
method, the method is simply a referenceTask
thet performs the code in the body of the method. So, *IT DOES NOT ACTUALLY RUN ASYNCHRONOUSLY
- The primary difference is that the code that you execute should return a value.
- It shall be used
Task<TResult>
- To
return
a value use theReturn
property of theTask<TResult>
.- If the task has not finished running the method and the result is not yet available, the
Result
property block the caller.
- If the task has not finished running the method and the result is not yet available, the
- An example is presented below:
Task<int> task2 = Task.Run(() => add(8,4));
int result = task2.Result;
Console.WriteLine(result);
int add(int a, int b)
{
return a+b;
}
Task<TResult>
is also the basis of the mechanism for defining asynchronous methods that return values.- If an asynchronous method actually generates a result, it should return a
Task<TResult>
- If an asynchronous method actually generates a result, it should return a
- An Asynchronous version of the previous code is presented below:
- To invoke an asynchronous method that returns a value it shall be used
await
otherwise it is a compiler error.- In the comments it is presented an alternativeway to invoke the method!
- To invoke an asynchronous method that returns a value it shall be used
int result = await addAsync(7, 3); // invokes the asynchronous method using await
Console.WriteLine($"Result: {result}");
//Alternative way...
//Console.WriteLine($"Result: {addAsync(7, 3)}"); it will print the Task id
//Console.WriteLine($"Result: {await addAsync(7, 3)}"); it waits until the task return a value and prints it
async Task<int> addAsync(int a, int b)
{
Task<int> t = Task.Run(() => add(a, b));
await t; //Returns a Task<int>
return t.Result; //Return an int
}
int add(int a, int b)
{
return a + b;
}
- An
async method
does not mean that it runs asynchronously.- It means that the metof can contain statements that may run asynchronously
- The
await
operator is different from theWait
method of aTask
Wait
method always blocks the current thread.- Also, it does not allow to reuse the thread until the task completes.
- The
await
operator indicates that a method should be run by a separate taks.- So, its calling code is suspended until the method call completes.
- The thread used by the calling code is release and can be reused. So:
- So, if the thread that is running the UI calls an async method,
- it enters to its statemens until it fins an
await
- Then is release from the
async methods
and - is reused by the UI.
- The thread used by the calling code is release and can be reused. So:
- So, its calling code is suspended until the method call completes.