Skip to content

Instantly share code, notes, and snippets.

@c4tachan
Last active September 19, 2019 20:31
Show Gist options
  • Save c4tachan/2b09e4cd0cd59ceda2bdcaf9e47c3201 to your computer and use it in GitHub Desktop.
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.
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