Last active
September 19, 2019 20:31
-
-
Save c4tachan/2b09e4cd0cd59ceda2bdcaf9e47c3201 to your computer and use it in GitHub Desktop.
Example program to demonstrate/experiment with how async/await works and what happens if you make mistakes. There are intentionally compiler warnings in this code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using System.Reflection; | |
namespace AsyncAwaitExperimentation | |
{ | |
class Program | |
{ | |
static async Task Main(string[] args) | |
{ | |
Console.WriteLine("This program will demonstrate how Async/Await works.\n\n"); | |
var waiter = new Waiters(); | |
Task[] tasks = new Task[4]; | |
// This is just making sure we know what thread the Main function is running on. | |
waiter.PrintThread("Main "); | |
// Call A will proceed in the main thread until it reaches the await Thread.Delay(10) call. | |
// The main thread will then immediately proceed to the next line of code in the Main Function. | |
// Execution of the A call will continue once the delay of 10 ms has elapsed. | |
tasks[0] = waiter.func("A"); | |
// This is just making sure we know what thread the Main function is running on. | |
waiter.PrintThread("Main "); | |
// The NoAwait function is written as if it were intended to be async, but it does not | |
// contain any uses of the await keyword. Because of this, we will see that the start | |
// and end messages in the function will occur synchronously and on the same thread. | |
tasks[1] = waiter.NoAwait(); | |
// This is just making sure we know what thread the Main function is running on. | |
waiter.PrintThread("Main "); | |
// The MultilevelAwait function calls several async functions inside of itself. | |
tasks[2] = waiter.MultilevelAwait(); | |
// This is just making sure we know what thread the Main function is running on. | |
waiter.PrintThread("Main "); | |
// This function makes use of the Task.WaitAll() function to experiment with the | |
// Task-based Asynchronous Programing Pattern. | |
tasks[3] = waiter.WaitallCalled(); | |
// This is just making sure we know what thread the Main function is running on. | |
waiter.PrintThread("Main "); | |
await Task.WhenAll(tasks); | |
Console.WriteLine("\n\nThis is the final line of the Main function on Thread {0}", Thread.CurrentThread.ManagedThreadId); | |
} | |
} | |
class Waiters | |
{ | |
public void PrintThread(string func) | |
{ | |
string tabs = ""; | |
for(int i = Thread.CurrentThread.ManagedThreadId; i > 0; i--) | |
{ | |
tabs += "\t"; | |
} | |
Console.WriteLine("{0}{1} {2}", tabs, func, Thread.CurrentThread.ManagedThreadId); | |
} | |
// Function A prints out a message prior to it's await statement. This message is printed on the thread of the calling function. | |
// After the await, execution proceeds on a free or new thread which is where the function prints a second message. | |
public async Task func(string fn, string i = " ") | |
{ | |
PrintThread(String.Format("{0}{1}-Str ", fn, i)); | |
await Task.Delay(1); | |
Thread.Sleep(1); | |
PrintThread(String.Format("{0}{1}-End ", fn, i)); | |
} | |
// Function B contains no await keyword, so it always runs synchronously, even though it is modified by the async keyword. | |
// If we have written a function like this intentionally, then we would have been better off simply removing the async modifier | |
// from the function declaration. | |
public async Task NoAwait() | |
{ | |
PrintThread("B -Str "); | |
PrintThread("B -End "); | |
} | |
// Function MultilevelAwait demonstrates what happens if we do not use the await keyword when calling an awaitable function. | |
// In this case, the D call returns to this stack frame at the await keyword in func() and execution in this function continues. | |
// As a result it is possible for the D-end message to print after the C-pstD. | |
// The use of await on the E call forces this function to wait until execution of the E call has completed all the way to the | |
// return before the C-pstE message is printed. | |
public async Task MultilevelAwait() | |
{ | |
PrintThread("C -Str "); | |
await Task.Delay(1); | |
PrintThread("C -preD"); | |
func("D"); | |
PrintThread("C -pstD"); | |
await func("E"); | |
PrintThread("C -pstE"); | |
} | |
// The intention of this function is to spin up several threads and just generally make a mess of things. | |
public async Task WaitallCalled() | |
{ | |
PrintThread("F -Str "); | |
Task[] tasks = new Task[10]; | |
for(int i = 0; i < 10; i++) | |
{ | |
tasks[i] = func("G", i.ToString()); | |
} | |
PrintThread("F -Mid "); | |
await Task.WhenAll(tasks); | |
PrintThread("F -End "); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment