First thing first, we should have basic understanding of difference between asynchronous programming and multithreading?. Please go through it as it will clear some of the questions on your mind.
Here is an analogy that may help understand the problem. You are cooking in a restaurant. An order comes in for eggs and toast.
- Synchronous - you cook the eggs, then you cook the toast.
- Asynchronous, single threaded - you start the eggs cooking and set a timer. You start the toast cooking, and set a timer. While they are both cooking, you clean the kitchen. When the timers go off you take the eggs off the heat and the toast out of the toaster and serve them.
- Asynchronous, multi-threaded - you hire two more cooks, one to cook eggs and one to cook toast. Now you have the problem of coordinating the cooks so that they do not conflict with each other in the kitchen when sharing resources. And you have to pay them.
So in brief, Threading is about workers, Asynchronous is about tasks.
In this article, I've tried to solve the common problem of working with a public list or a resource using asynchronous calls in a multi-threaded program. I have demonstrated same problem with a use case where I will create 2 users and they will run multiple threads and simultaneously will read and print public list on console.
We follow these simple steps,
- Create a public list that has to be shared by multiple users running on separate threads.
- Creating multiple users
- Running each user on a separate thread
- Asynchronous call to print method where we have implemented different time delay’s for each user to simulate real-time scenario of asynchronous calls.
Here , I have created separate methods to simplify my task and for better understanding of code.
Step 1
We will start by creating a list that will be shared by multiple users. Then we will call CreateUsers().
- class Program {
- public static List < string > UserList;
- static void Main(string[] args) {
- Console.WriteLine("Main Thread Id - {0}", Thread.CurrentThread.ManagedThreadId);
- UserList = new List < string > ();
- UserList.Add("A");
- UserList.Add("B");
- UserList.Add("C");
- UserList.Add("D");
- UserList.Add("E");
- UserList.Add("F");
- UserList.Add("G");
- UserList.Add("H");
- CreateUsers();
- Console.WriteLine("Completed main thread !!");
- Console.ReadKey();
- }
- }
Step 2
In this method, we can read users data from a .csv file or database and based on their count. We can create different threads for them by passing their object to the next method, i.e createthread().
- private static void CreateUsers() {
- Console.WriteLine();
- Console.WriteLine("Creating user() Thread Id - {0}", Thread.CurrentThread.ManagedThreadId);
-
-
- for (int i = 0; i < 2; i++) {
-
-
- int tempUser = i;
- createThread(tempUser);
- }
- }
Step 3
Here we will create a separate thread for each user and pass user data to Print() method.
- private static void createThread(int tempUser) {
- Console.WriteLine();
- Console.WriteLine("createthread() Thread Id - {0} for user id - {1} ", Thread.CurrentThread.ManagedThreadId, tempUser);
- new Thread(() => Print(tempUser)).Start();
- }
Step 4
This Print() will call asynchronous method and will wait before printing its completion message to console. Notice this method will be running on separate threads simultaneously.
- private static void Print(int tempUser) {
- Console.WriteLine();
- Console.WriteLine("Print() Thread Id - {0} for user id - {1} ", Thread.CurrentThread.ManagedThreadId, tempUser);
- new Program().Print_success(tempUser).Wait();
- Console.WriteLine("printing for user {0} completed with Thread id- {1} !!", tempUser, Thread.CurrentThread.ManagedThreadId);
- }
Step 5
Now we will create our Asynchronous Print_Success() that performs task of printing data from the list and here to demonstrate different delays in real-time async tasks we will make our threads sleep for different duration’s.
- public async Task Print_success(int tempUser) {
- Console.WriteLine();
- Console.WriteLine("Print_success() Thread Id - {0} for user id - {1} ", Thread.CurrentThread.ManagedThreadId, tempUser);
- await Task.Run(() => {
- foreach(var item in UserList) {
- if (tempUser == 0) {
- Thread.Sleep(10000);
- Console.WriteLine("User: {0} says- {1} on Thread ID - {2}", tempUser, item, Thread.CurrentThread.ManagedThreadId);
- } else {
- Thread.Sleep(5000);
- Console.WriteLine("User: {0} says- {1} on Thread ID - {2}", tempUser, item, Thread.CurrentThread.ManagedThreadId);
- }
- }
- });
- }
Output
- In Output, we print Thread Id of main() and other functions that we have called.
- We can notice that our functions before calling new thread() that have same thread Id but once new thread() is called functions after that are running on separate thread ID’s
- We can also see delay in our output from asynchronous Print_Success() call.