What is Concurrency and Why you should care about it?

What is Concurrency and Why you should care about it?

What is Concurrency?

Concurrency is about progressing with two or more activities at the same time. To better understand the above definition, let's have a look at a real-world analogy.

You want to surprise your mother on her birthday by baking something delicious for her. From her past birthday experiences, you know that she loves both chocolate cake and apple pie, equally. So you decide to bake both for her birthday.

Eventually, you have two tasks which need to be dealt with on the same day:

  • Bake a delicious Chocolate cake
  • Bake a gratifying Apple pie

You can complete both the tasks by following any one of the following scenarios.

Scenario 1

You take out ingredients from your refrigerator and start baking the cake. When you leave the cake in the oven, you simply wait for it to bake and do nothing in between. When it's done, you take it out, add a layer of delicious chocolates on top of it and give some final touches. Congratulations you just made a delicious and gratifying chocolate cake. As you have already made the chocolate cake, you start preparing the apple pie. The apple pie takes a while to complete. This time too, you just simply wait till the apple pie gets prepared. This way, both tasks are complete.

What you do is that you simply complete each task sequentially, nothing fancy. You wait for one task to complete and then only you pick another task. The progress is only being made on one task at a time.

Scenario 2

You start in the same way as earlier but this time you don't wait for one task to complete in order to pick up another task. In other words, when you put the chocolate cake into the oven for baking, instead of just waiting for the time to come to take out the cake from the oven, you decide to utilize this window and start preparing the apple pie. Whenever you are waiting on one task you switch to another task. In this way, you are progressing on both the tasks at the same time. As a result, the chocolate cake and apple pie will be ready much quicker.

Scenario 3

You ask your brother to help you with baking and he agrees. He starts working on the apple pie while you are working on the chocolate cake. Your brother is a talented chef just like you so he doesn't need any instruction from you while preparing the apple pie. You both finish your tasks independently. In this way too, progress is being made on both the tasks at the same time and you don't need to juggle between the tasks. Moreover, you are able to use an extra pair of hands to complete both the tasks by asking your brother for help.

Now, have you figured out which way of execution can be called a concurrent execution? Yes, Scheme 2 and Scheme 3 are examples of concurrent ways of executions.

   

Concurrency in Computer Universe

Let's map the above examples to a computer science universe.

Scenario 1, Before the origination of multitasking in operating systems, the operating system used to complete tasks one by one. When a task involves a lot of I/O operations the CPU has to waste a lot of time in an idle state while waiting for those I/O operations to complete.

Scenario 2, To increase the utilization of the CPU, humanity invented multitasking. In a single processor and single-core machine, instead of waiting on I/O operations, the operating system schedules another task on the CPU while the former task completes its I/O operations. In this way, the time spent waiting on I/O operations is significantly reduced and the utilization of the CPU is also increased.

Scenario 3, We are now in the 21st century, and our machines are more powerful than ever. These days, machines come with multiprocessors and multi-core hardware technology. Now a capable operating system can process more than one task at the same time by scheduling them on different cores independently. It is also known as parallelization.

   

Why should one implement concurrency?

There are mainly two reasons to implement concurrency in applications.

Concurrency for Better Performance

As we know that our modern machines come with hardware support of more than one processor and core, instead of running a single task at a time, we can use the power of multiple cores and run multiple tasks in parallel. By progressing through multiple tasks at the same time, we can increase the efficiency of our applications manyfold. But we should also keep in mind that not every task can be parallelized.

Concurrency for Separation of Concerns

You can use concurrency to execute different parts of an application separately. Separation of concern is always a good idea in software engineering. This makes your code easier to understand and test, thus less prone to bugs.

   

Challenges associated with Concurrency

No doubt, concurrency can unlock substantial benefits in an application's efficiency, however, writing good concurrent applications is hard.

Concurrency also comes with various challenges pertaining to data synchronization. If one is trying to run multiple tasks concurrently and those tasks need access to a piece of shared data, then things might go wrong in very outstanding and astonishing ways.

Below are some of the most frequently encountered problems by the developers while writing concurrent applications.

Data race

When two or more tasks modify a shared piece of data at the same time without any data synchronization between them then the outcome depends on the ordering of the operations.

Let's understand the data race with a real-life example. Suppose you have $1000 in the bank. You made a transfer of $2000 to somebody else and at the same time, someone else sent you $5000. Now inside the bank, employee A saw your initial balance of $1000 and adds $5000 to it. At the same time, employee B saw your current balance at $1000 and subtracts $2000.

Employee A and Employee B don't cooperate and overwrites each other's updates on the ledger(yeah, bad bank). Employee A updated your balance as $6000 in the ledger. Employee B got hold of the ledger and updated your account balance as -$1000. You lost money in between. As you can see if employee B updates the account first and then employee A updates it then the final account balance will be something else. Here the final result depends upon the order of execution.

The same happens with computers. Two tasks trying to write a piece of memory at the same time without any synchronization between them can lead to behavior which depends on the order of execution, thus undefined behavior.

Deadlock

It happens when two or more tasks wait for a shared resource to be freed from the other while holding another shared resource required by the other.

Suppose task A needs resource P and Q to finish its execution. Currently, A has only access to P and waits for Q to be freed from task B. Meanwhile, B also needs resource P and Q to finish its execution and currently holding on to resource Q while waiting for P to be freed from task A. As you see A is waiting for B and B is waiting for A simultaneously, this situation is known as deadlock.

Untitled Diagram (2).png

There are also other problems like livelock , resource starvation , priority inversion which you may face while writing concurrent applications. We will discuss the solution to the above problems with example code written in GO in a different article.

   

Conclusion

Yes, writing a concurrent application is hard. However, a carefully written concurrent application can unlock a significant increase in performance which was not possible before. Therefore it's worth using concurrency on those performance-critical parts of applications where there's potential for any measurable gain.