Skip to main content

General Concepts

Race Condition

Introduction

One of the most common problems in multithreaded applications is the problem of race conditions.

In this tutorial, we’ll learn what race conditions are and how are they introduced into the system.

What is a Race Condition?

race condition occurs when two or more threads have access to the same data and both try to update it simultaneously.

Let's take a block of code and try to understand what can go wrong with the below code and what the Race Condition is.

func Transfer(amount, accountA, accountB) {
    if accountA.amount < amount {
        return
    }
    
    accountB += amount
    accountA -= amount
}

Synchronous Transactions

Let's assume we have 500 dollars in both accounts. And we are attempting to perform 2 different transactions one after the other.

In the first transaction, we transfer 200 dollars from Account A to Account B. In the second transaction, we try to transfer 400 dollars from Account A to Account B.

Two Transactions. One after the other.

The above image shows how two transactions are performed one after the other. And this is something that we are expecting as well.

Asynchronous Transactions

As long as the transactions are happening synchronously, there will be no problem at all. But if both these transactions are carried out asynchronously. There will be a problem. Let's see how.

Race Condition

As you can see, when both these transactions are running asynchronously, the final results can be wrong and this situation is known as Race Condition.

💡
Thread-safe is the term we use to describe a program, code, or data structure free of race conditions when accessed by multiple threads.

How can we make sure that our program is Thread Safe?

There are two kinds of approaches to fighting race conditions:

  • Avoiding Shared State
  • Using Synchronizations and Atomic Operations

We will learn about them in the upcoming chapters.