C Academy · Lesson

Synchronization and Race Conditions

Implement mutexes and semaphores to manage shared resources safely.

Lesson 3 of 310 steps

Synchronization and Race Conditions is a free C Academy lesson on CoddyKit. This is lesson 3 of 3. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C Academy learning path, and your progress syncs across the web and the CoddyKit app. The C Academy course includes 3 lessons in total.

Synchronization and Race Conditions

When multiple threads access shared resources, race conditions can occur, leading to unpredictable results.

In this lesson, you will learn:

  • What race conditions are and how they occur.
  • How to use mutexes to prevent race conditions.
  • How semaphores help manage shared resources.
Synchronization and Race Conditions — illustration 1

What is a Race Condition?

A race condition occurs when multiple threads access shared data simultaneously, leading to unpredictable behavior.

Example:

  • Two threads try to update a global counter.
  • The final value may be incorrect due to interference.

Example: Race Condition in Multithreading

This program demonstrates a race condition where multiple threads modify a shared variable.

#include <stdio.h>
#include <pthread.h>

int counter = 0;

void *incrementCounter(void *arg) {
    for (int i = 0; i < 1000000; i++) {
        counter++;
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, incrementCounter, NULL);
    pthread_create(&t2, NULL, incrementCounter, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Final counter value: %d\n", counter);
    return 0;
}

Preventing Race Conditions with Mutex

A mutex (mutual exclusion) ensures only one thread accesses a resource at a time.

Mutex functions:

  • pthread_mutex_init() - Initializes a mutex.
  • pthread_mutex_lock() - Locks the mutex.
  • pthread_mutex_unlock() - Unlocks the mutex.
  • pthread_mutex_destroy() - Destroys the mutex.

Example: Using Mutex to Prevent Race Conditions

This program fixes the race condition by using a mutex to control access to the shared counter.

#include <stdio.h>
#include <pthread.h>

int counter = 0;
pthread_mutex_t lock;

void *incrementCounter(void *arg) {
    for (int i = 0; i < 1000000; i++) {
        pthread_mutex_lock(&lock);
        counter++;
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_mutex_init(&lock, NULL);
    pthread_create(&t1, NULL, incrementCounter, NULL);
    pthread_create(&t2, NULL, incrementCounter, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&lock);
    printf("Final counter value: %d\n", counter);
    return 0;
}

What is a Semaphore?

A semaphore is a counter used to control access to resources.

Key functions:

  • sem_init() - Initializes a semaphore.
  • sem_wait() - Decreases the semaphore value.
  • sem_post() - Increases the semaphore value.
  • sem_destroy() - Destroys the semaphore.

Example: Using Semaphores for Synchronization

This program uses a semaphore to manage access to a shared resource.

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem;

void *task(void *arg) {
    sem_wait(&sem);
    printf("Thread %d is executing\n", *(int *)arg);
    sem_post(&sem);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    int id1 = 1, id2 = 2;
    sem_init(&sem, 0, 1);
    pthread_create(&t1, NULL, task, &id1);
    pthread_create(&t2, NULL, task, &id2);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    sem_destroy(&sem);
    return 0;
}

Best Practices for Thread Synchronization

To avoid race conditions:

  • Use mutexes to protect shared variables.
  • Use semaphores for resource management.
  • Minimize shared resource usage where possible.

Summary

In this lesson, you learned:

  • What race conditions are and how they occur.
  • How to use mutexes to prevent race conditions.
  • How semaphores help manage access to shared resources.

This concludes the Multithreading and Concurrency section in C!

Synchronization and Race Conditions — illustration 10
Free to start

Learn C with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
39
Lessons
144

Frequently Asked Questions

Is the “Synchronization and Race Conditions” lesson free?

Yes — the full text of “Synchronization and Race Conditions” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C Academy course, upgrade to CoddyKit PRO. The C Academy course includes 3 lessons in total.

What will I learn in “Synchronization and Race Conditions”?

Implement mutexes and semaphores to manage shared resources safely. You practise C Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C Academy?

No prior experience is required. C Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 3.

How long does the “Synchronization and Race Conditions” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C Academy lesson?

Yes. Every C Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Introduction to Threads
  2. Creating and Managing Threads
  3. Synchronization and Race Conditions
← Back to C Academy