Thread Synchronization

🧪 Lab 2 — Techniques for Thread Synchronization

Duration: ~1 hour
Audience: Undergraduate students familiar with basic threading (Lab 1)
Languages: C (POSIX Threads), optional Python demos
Environment: Linux / Windows (WSL recommended)


🎯 Learning Objectives

By the end of this lab, students will be able to:

  1. Use mutexes, condition variables, and barriers to coordinate threads.
  2. Identify and prevent race conditions and deadlocks.
  3. Understand how condition variables work internally.
  4. Apply synchronization techniques to real-world “smart environment” scenarios
    such as smart farming, smart homes, and smart buildings.

🧠 Concept Recap

Primitive Purpose Example Scenario
Mutex Protect shared data Shared bank account, temperature log
Condition Variable Wait for a condition to be met Producer–consumer, smart sensors
Barrier Wait for all threads to reach a point Periodic data aggregation
Atomic Ops Fast counter / flag updates Counting detected motion events

🧩 Understanding Condition Variables

A condition variable lets threads wait for a condition to become true
while temporarily releasing a lock (mutex) so that other threads can change that condition.

Think of it as:

“If the fridge is empty, the consumer waits.
When the producer refills it, it notifies the consumer.”


⚙️ The Three Key Operations

Function Description
pthread_cond_wait(&cond, &mutex) Waits for a condition. Temporarily releases the mutex while sleeping, and reacquires it before returning.
pthread_cond_signal(&cond) Wakes up one waiting thread.
pthread_cond_broadcast(&cond) Wakes up all waiting threads.

💡 Typical Pattern

pthread_mutex_lock(&mtx);
while (!condition)
    pthread_cond_wait(&cond, &mtx);
// perform work once condition true
pthread_mutex_unlock(&mtx);

🔁 Why the while loop?

Even after being signaled, a waiting thread must re-check the condition, because:

  • Another thread may have modified it before this one resumed.
  • Some systems allow spurious wakeups (wakeups without signals).

Hence we use:

while (condition_not_true)
    pthread_cond_wait(&cond, &mutex);

🧩 Mini Example - cond_example.c

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

int data_ready = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  c = PTHREAD_COND_INITIALIZER;

void* producer(void* arg) {
    sleep(1);
    pthread_mutex_lock(&m);
    data_ready = 1;
    printf("Producer: data is ready!\n");
    pthread_cond_signal(&c);
    pthread_mutex_unlock(&m);
    return NULL;
}

void* consumer(void* arg) {
    pthread_mutex_lock(&m);
    while (!data_ready) {
        printf("Consumer: waiting...\n");
        pthread_cond_wait(&c, &m);
    }
    printf("Consumer: got the data!\n");
    pthread_mutex_unlock(&m);
    return NULL;
}

int main() {
    pthread_t prod, cons;
    pthread_create(&cons, NULL, consumer, NULL);
    pthread_create(&prod, NULL, producer, NULL);
    pthread_join(prod, NULL);
    pthread_join(cons, NULL);
}

Build & Run

gcc -pthread -o cond_example cond_example.c
./cond_example

Output

Consumer: waiting...
Producer: data is ready!
Consumer: got the data!

✅ The consumer waits efficiently — it’s not busy-waiting.

🧩 Part 1 — Smart Farm Sensor Fusion (using a Barrier)

🌾 Scenario

A smart farm has multiple sensors (temperature, humidity, soil moisture). Each thread reads data from one sensor. After all sensors report, the system fuses the data to make an irrigation decision.

🧰 Code — smartfarm_barrier.c

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

#define NUM_SENSORS 3

typedef struct {
    int id;   // sensor id
} SensorArg;

pthread_barrier_t barrier;
int readings[NUM_SENSORS];

void* sensor(void* arg) {
    SensorArg* a = (SensorArg*)arg;
    int id = a->id;

    // Simulate variable reading time
    sleep(1 + id);

    readings[id] = 20 + id * 5;  // fake data - we could use rand here with
                                // To get new random readings on each run.
    printf("Sensor %d reports %d C\n", id, readings[id]);

    // Wait for all sensors to finish their reading
    pthread_barrier_wait(&barrier);

    // One thread (e.g., id 0) performs fusion/decision after the barrier
    if (id == 0) {
        int sum = 0;
        for (int i = 0; i < NUM_SENSORS; i++) sum += readings[i];
        int avg = sum / NUM_SENSORS;
        printf("Fusion thread: average temp = %d C. Output: %s\n",
               avg, (avg > 25 ? "ON" : "OFF"));
    }
    return NULL;
}

int main(void) {
    pthread_t threads[NUM_SENSORS];
    SensorArg args[NUM_SENSORS];

    pthread_barrier_init(&barrier, NULL, NUM_SENSORS);


    for (int i = 0; i < NUM_SENSORS; i++) {
        args[i].id = i;
        pthread_create(&threads[i], NULL, sensor, &args[i]);
    }

    for (int i = 0; i < NUM_SENSORS; i++) {
        pthread_join(threads[i], NULL);
    }

    pthread_barrier_destroy(&barrier);
    return 0;
}

Build & Run:

gcc -pthread -o smartfarm_barrier smartfarm_barrier.c
./smartfarm_barrier

Discussion

What happens if the barrier is removed?

🧩 Part 2 — Smart Home Producer–Consumer (using Condition Variables)

🏠 Scenario

A smart-home gateway receives sensor events (producer) and processes them (consumer). We’ll implement a simple bounded buffer using a mutex and condition variables.

🧰 Code — smart_home_pc.c

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

#define BUFFER_SIZE 5
#define NUM_EVENTS 20

int buffer[BUFFER_SIZE];
int count = 0, in = 0, out = 0;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_full  = PTHREAD_COND_INITIALIZER;

void* producer(void* arg) {
    for (int i = 0; i < NUM_EVENTS; i++) {
        pthread_mutex_lock(&mtx);
        while (count == BUFFER_SIZE)
            pthread_cond_wait(&not_full, &mtx);

        buffer[in] = i;
        in = (in + 1) % BUFFER_SIZE;
        count++;
        printf("[Producer] Sensor event %d produced (count=%d)\n", i, count);

        pthread_cond_signal(&not_empty);
        pthread_mutex_unlock(&mtx);
        usleep(100000);
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 0; i < NUM_EVENTS; i++) {
        pthread_mutex_lock(&mtx);
        while (count == 0)
            pthread_cond_wait(&not_empty, &mtx);

        int item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        printf("              [Consumer] processed event %d (count=%d)\n", item, count);

        pthread_cond_signal(&not_full);
        pthread_mutex_unlock(&mtx);
        usleep(150000);
    }
    return NULL;
}

int main() {
    pthread_t prod, cons;
    pthread_create(&prod, NULL, producer, NULL);
    pthread_create(&cons, NULL, consumer, NULL);
    pthread_join(prod, NULL);
    pthread_join(cons, NULL);
}

Build & Run:

gcc -pthread -o smart_home_pc smart_home_pc.c
./smart_home_pc

Output

One of the possible outputs.

PS D:\UOP\Course\lab\lab2> ./smart_home
[Producer] Sensor event 0 produced (count=1)
              [Consumer] processed event 0 (count=0)
[Producer] Sensor event 1 produced (count=1)
              [Consumer] processed event 1 (count=0)
[Producer] Sensor event 2 produced (count=1)
[Producer] Sensor event 3 produced (count=2)
              [Consumer] processed event 2 (count=1)
[Producer] Sensor event 4 produced (count=2)
              [Consumer] processed event 3 (count=1)
[Producer] Sensor event 5 produced (count=2)
[Producer] Sensor event 6 produced (count=3)
              [Consumer] processed event 4 (count=2)
[Producer] Sensor event 7 produced (count=3)
              [Consumer] processed event 5 (count=2)
[Producer] Sensor event 8 produced (count=3)
              [Consumer] processed event 6 (count=2)
[Producer] Sensor event 9 produced (count=3)
[Producer] Sensor event 10 produced (count=4)
              [Consumer] processed event 7 (count=3)
[Producer] Sensor event 11 produced (count=4)
              [Consumer] processed event 8 (count=3)
[Producer] Sensor event 12 produced (count=4)
[Producer] Sensor event 13 produced (count=5)
              [Consumer] processed event 9 (count=4)
[Producer] Sensor event 14 produced (count=5)
              [Consumer] processed event 10 (count=4)
[Producer] Sensor event 15 produced (count=5)
              [Consumer] processed event 11 (count=4)
[Producer] Sensor event 16 produced (count=5)
              [Consumer] processed event 12 (count=4)
[Producer] Sensor event 17 produced (count=5)
              [Consumer] processed event 13 (count=4)
[Producer] Sensor event 18 produced (count=5)
              [Consumer] processed event 14 (count=4)
[Producer] Sensor event 19 produced (count=5)
              [Consumer] processed event 15 (count=4)
              [Consumer] processed event 16 (count=3)
              [Consumer] processed event 17 (count=2)
              [Consumer] processed event 18 (count=1)
              [Consumer] processed event 19 (count=0)

🧩 Part 3 — Smart Greenhouse: Producer, Consumer and Monitor

🌿 Scenario

A smart greenhouse has multiple temperature sensors (producers) generating readings, actuators (consumers) adjusting conditions, and a monitor thread that periodically checks the latest temperature to detect unsafe trends.

🧰 Code — smart_greenhouse.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

// ============================ Config =============================
#define BUFFER_SIZE    5
#define NUM_READINGS   5
#define SAFE_TEMP_MIN  18
#define SAFE_TEMP_MAX  28

// ANSI colors
#define RED    "\033[1;31m"   // Producer (Sensor)
#define BLUE   "\033[1;34m"   // Actuator (Consumer)
#define GREEN  "\033[1;32m"   // Monitor
#define RESET  "\033[0m"

// =========================== Commands ============================
typedef enum {
    CMD_NONE = 0,
    CMD_COOL_ON,
    CMD_HEAT_ON,
    CMD_STABLE,
    CMD_SHUTDOWN
} command_t;

// ======================= Shared: readings buf ====================
static int buffer[BUFFER_SIZE];
static int count = 0;

static int producer_done = 0;

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  not_empty = PTHREAD_COND_INITIALIZER;
static pthread_cond_t  not_full  = PTHREAD_COND_INITIALIZER;

// ==================== Shared: monitor→actuator cmd ===============
static command_t cmd_mailbox = CMD_NONE;
static int cmd_has_msg = 0;

static pthread_mutex_t cmd_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  cmd_cv  = PTHREAD_COND_INITIALIZER;

// ============================ Producer ===========================
static void* producer(void* arg) {
    int in_idx = 0;

    for (int i = 0; i < NUM_READINGS; i++) {
        int temp = 15 + rand() % 15; // [15..29]

        // push temp into ring buffer
        pthread_mutex_lock(&mtx);
        while (count == BUFFER_SIZE)
            pthread_cond_wait(&not_full, &mtx);

        buffer[in_idx] = temp;
        in_idx = (in_idx + 1) % BUFFER_SIZE;
        count++;
        pthread_cond_signal(&not_empty);
        pthread_mutex_unlock(&mtx);

        // Print outside lock
        printf(RED "[Sensor] Produced temp = %dC (count=%d)\n" RESET, temp, count);
        usleep(150000); //Modifying this will change the rate of how fast are genereated
    }

    // mark done and wake any waiters
    pthread_mutex_lock(&mtx);
    producer_done = 1;
    pthread_cond_broadcast(&not_empty);
    pthread_mutex_unlock(&mtx);

    return NULL;
}

// ============================ Monitor ============================
// Consumes readings from buffer, computes decision, signals actuator.
static void* monitor(void* arg) {
    
    int out_idx = 0;
    
    while (1) {
        int temp = 0;
        int done = 0;

        // Pop one reading (or exit if empty & producer_done)
        pthread_mutex_lock(&mtx);
        while (count == 0 && !producer_done)
            pthread_cond_wait(&not_empty, &mtx);

        if (count == 0 && producer_done) {
            done = 1;
        } else {
            temp = buffer[out_idx];
            out_idx = (out_idx + 1) % BUFFER_SIZE;
            count--;
            pthread_cond_signal(&not_full);
        }
        pthread_mutex_unlock(&mtx);

        if (done) break;

        // Decide command based on temp
        command_t cmd;
        if (temp > SAFE_TEMP_MAX)       cmd = CMD_COOL_ON;
        else if (temp < SAFE_TEMP_MIN)  cmd = CMD_HEAT_ON;
        else                            cmd = CMD_STABLE;

        // Announce decision (outside locks)
        if (cmd == CMD_COOL_ON)
            printf(GREEN "                          [Monitor] temp=%dC . COOL ON\n" RESET, temp);
        else if (cmd == CMD_HEAT_ON)
            printf(GREEN "                          [Monitor] temp=%dC . HEAT ON\n" RESET, temp);
        else
            printf(GREEN "                          [Monitor] temp=%dC . STABLE\n" RESET, temp);

        // Post command to actuator (single-message mailbox)
        pthread_mutex_lock(&cmd_mtx);
        while (cmd_has_msg) // wait until previous command is taken
            pthread_cond_wait(&cmd_cv, &cmd_mtx);

        cmd_mailbox = cmd;
        cmd_has_msg = 1;
        pthread_cond_signal(&cmd_cv);
        pthread_mutex_unlock(&cmd_mtx);
    }

    // Send shutdown command
    pthread_mutex_lock(&cmd_mtx);
    while (cmd_has_msg)
        pthread_cond_wait(&cmd_cv, &cmd_mtx);
    cmd_mailbox = CMD_SHUTDOWN;
    cmd_has_msg = 1;
    pthread_cond_signal(&cmd_cv);
    pthread_mutex_unlock(&cmd_mtx);

    return NULL;
}

// ============================ Actuator ===========================
// Waits for commands from Monitor and performs actions.
static void* actuator(void* arg) {
    (void)arg;
    while (1) {
        command_t cmd;

        // Wait for a command
        pthread_mutex_lock(&cmd_mtx);
        while (!cmd_has_msg)
            pthread_cond_wait(&cmd_cv, &cmd_mtx);

        cmd = cmd_mailbox;
        cmd_has_msg = 0;
        pthread_cond_signal(&cmd_cv); // let monitor post next
        pthread_mutex_unlock(&cmd_mtx);

        // Act on command
        if (cmd == CMD_SHUTDOWN) {
            printf(BLUE "             [Actuator] Shutdown received. Exiting.\n" RESET);
            break;
        } else if (cmd == CMD_COOL_ON) {
            printf(BLUE "             [Actuator] Cooling ON\n" RESET);
        } else if (cmd == CMD_HEAT_ON) {
            printf(BLUE "             [Actuator] Heating ON\n" RESET);
        } else if (cmd == CMD_STABLE) {
            printf(BLUE "             [Actuator] Systems stable\n" RESET);
        }
        usleep(200000);
    }
    return NULL;
}

// ============================== Main ============================
int main(void) {
    srand((unsigned)time(NULL));

    pthread_t prod_th, mon_th, act_th;
    pthread_create(&prod_th, NULL, producer, NULL);
    pthread_create(&mon_th,  NULL, monitor,  NULL);
    pthread_create(&act_th,  NULL, actuator, NULL);

    pthread_join(prod_th, NULL);
    pthread_join(mon_th,  NULL);
    pthread_join(act_th,  NULL);

    printf("\nMain: All threads finished. Exiting...\n");
    return 0;
}

Build & Run

gcc -pthread -o smart_greenhouse smart_greenhouse.c
./smart_greenhouse

Output

[Sensor] Produced temp = 26C (count=1)

[Monitor] temp=26C . STABLE

[Actuator] Systems stable

[Sensor] Produced temp = 17C (count=1)

[Monitor] temp=17C . HEAT ON

[Actuator] Heating ON

[Sensor] Produced temp = 19C (count=1)

[Monitor] temp=19C . STABLE

[Actuator] Systems stable

[Sensor] Produced temp = 25C (count=1)

[Monitor] temp=25C . STABLE

[Actuator] Systems stable

[Sensor] Produced temp = 29C (count=0)

[Monitor] temp=29C . COOL ON

[Actuator] Cooling ON

[Actuator] Shutdown received. Exiting.

Expected Behavior

  • The sensor produces readings.
  • The monitor consumes temperatures from the sensor buffer.
  • The Monitor decides what to do (cool, heat, stable).
  • Monitor signals the Actuator via a dedicated mailbox + condition variable, avoiding busy-waiting and minimizing lock contention.
  • The actuator consumes perform the action.

🧠 Key Takeaways

  • Synchronization ensures correctness but adds overhead.
  • Choose the right primitive:
    • 🧩 Mutex → exclusive access
    • 🔄 Condition Variables → coordination between threads
    • 🚧 Barriers → phase synchronization for groups of threads
  • Real-world smart systems (smart homes, farms, factories) rely on these to avoid data races and inconsistent decisions.

🐍 Appendix — Python Versions of the Synchronization Experiments

🧰 Setup

# (Optional) create a virtual environment
python -m venv .venv
# Windows
Run this command before activating your venv:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

.venv\Scripts\activate
# Linux/macOS
source .venv/bin/activate

# Install colorama (for colored terminal output)
pip install colorama

If you don’t want colors, remove the colorama parts and the ANSI constants.


A) Smart Farm Sensor Fusion (Barrier)

Idea: Same as the C barrier example — multiple sensors wait at a barrier, and after all arrive, one thread fuses the results.

File: smartfarm_barrier.py

import threading
import time
from random import randint
from colorama import init, Fore, Style

init(autoreset=True)

NUM_SENSORS = 3
readings = [None] * NUM_SENSORS
barrier = threading.Barrier(NUM_SENSORS)

def sensor(sensor_id: int):
    time.sleep(0.5 + sensor_id * 0.4)
    readings[sensor_id] = 20 + sensor_id * 5
    print(f"{Fore.RED}[Sensor {sensor_id}] reports {readings[sensor_id]}C{Style.RESET_ALL}")
    barrier.wait()
    if sensor_id == 0:
        avg = sum(readings) // NUM_SENSORS
        decision = "ON" if avg > 25 else "OFF"
        print(f"{Fore.GREEN}Fusion: avg={avg}C -> irrigation {decision}{Style.RESET_ALL}")

def main():
    threads = [threading.Thread(target=sensor, args=(i,)) for i in range(NUM_SENSORS)]
    for t in threads: t.start()
    for t in threads: t.join()

if __name__ == "__main__":
    main()

Run:

python smartfarm_barrier.py

B) Smart Home Producer–Consumer (Queue / Condition)

Idea: Same bounded-buffer behavior as the C version.
In Python, queue.Queue(maxsize=N) already handles the condition variable logic internally.

File: smart_home_pc.py

import time
import threading
import queue
from colorama import init, Fore, Style

init(autoreset=True)

BUFFER_SIZE = 5
NUM_EVENTS = 20
q = queue.Queue(maxsize=BUFFER_SIZE)

def producer():
    for i in range(NUM_EVENTS):
        q.put(i)
        print(f"{Fore.RED}[Producer] Sensor event {i} produced (count={q.qsize()}){Style.RESET_ALL}")
        time.sleep(0.1)

def consumer():
    for _ in range(NUM_EVENTS):
        item = q.get()
        print(f"{Fore.BLUE}              [Consumer] processed event {item} (count={q.qsize()}){Style.RESET_ALL}")
        time.sleep(0.15)
        q.task_done()

def main():
    t_prod = threading.Thread(target=producer)
    t_cons = threading.Thread(target=consumer)
    t_prod.start(); t_cons.start()
    t_prod.join(); q.join(); t_cons.join()

if __name__ == "__main__":
    main()

Run:

python smart_home_pc.py

C) Smart Greenhouse — Monitor Consumes and Commands Actuator

Idea:

  • Sensor (Producer) pushes temperatures into a readings queue.
  • Monitor consumes readings, decides actions, and posts commands to a command queue.
  • Actuator (Consumer) processes commands until shutdown.

File: smart_greenhouse.py

import threading
import queue
import time
from random import randint
from colorama import init, Fore, Style

init(autoreset=True)

READINGS = queue.Queue(maxsize=5)
COMMANDS = queue.Queue(maxsize=1)
NUM_READINGS = 20
SAFE_MIN = 18
SAFE_MAX = 28

CMD_COOL_ON = "COOL_ON"
CMD_HEAT_ON = "HEAT_ON"
CMD_STABLE = "STABLE"
CMD_SHUTDOWN = "SHUTDOWN"

def sensor():
    for _ in range(NUM_READINGS):
        temp = randint(15, 29)
        READINGS.put(temp)
        print(f"{Fore.RED}[Sensor] Produced temp = {temp}C (count={READINGS.qsize()}){Style.RESET_ALL}")
        time.sleep(0.15)
    READINGS.put(None)

def monitor():
    while True:
        temp = READINGS.get()
        if temp is None:
            COMMANDS.put(CMD_SHUTDOWN)
            print(f"{Fore.GREEN}                          [Monitor] SHUTDOWN sent{Style.RESET_ALL}")
            break
        if temp > SAFE_MAX:
            cmd, tag = CMD_COOL_ON, "COOL ON"
        elif temp < SAFE_MIN:
            cmd, tag = CMD_HEAT_ON, "HEAT ON"
        else:
            cmd, tag = CMD_STABLE, "STABLE"
        print(f"{Fore.GREEN}                          [Monitor] temp={temp}C . {tag}{Style.RESET_ALL}")
        COMMANDS.put(cmd)

def actuator():
    while True:
        cmd = COMMANDS.get()
        if cmd == CMD_SHUTDOWN:
            print(f"{Fore.BLUE}             [Actuator] Shutdown received. Exiting.{Style.RESET_ALL}")
            break
        elif cmd == CMD_COOL_ON:
            print(f"{Fore.BLUE}             [Actuator] Cooling ON{Style.RESET_ALL}")
        elif cmd == CMD_HEAT_ON:
            print(f"{Fore.BLUE}             [Actuator] Heating ON{Style.RESET_ALL}")
        elif cmd == CMD_STABLE:
            print(f"{Fore.BLUE}             [Actuator] Systems stable{Style.RESET_ALL}")
        time.sleep(0.2)

def main():
    t_s = threading.Thread(target=sensor)
    t_m = threading.Thread(target=monitor)
    t_a = threading.Thread(target=actuator)
    t_s.start(); t_m.start(); t_a.start()
    t_s.join(); t_m.join(); t_a.join()
    print("\nMain: All threads finished. Exiting...")

if __name__ == "__main__":
    main()

Run:

python smart_greenhouse.py

D) Condition Variable Mini Example

Idea: Demonstrates threading.Condition with the while/wait() pattern, equivalent to the C example.

File: cond_mini_example.py

import threading
import time
from colorama import init, Fore, Style

init(autoreset=True)

data_ready = False
cond = threading.Condition()

def producer():
    time.sleep(1)
    with cond:
        global data_ready
        data_ready = True
        print(f"{Fore.RED}Producer: data is ready!{Style.RESET_ALL}")
        cond.notify()

def consumer():
    with cond:
        while not data_ready:
            print(f"{Fore.BLUE}Consumer: waiting...{Style.RESET_ALL}")
            cond.wait()
        print(f"{Fore.BLUE}Consumer: got the data!{Style.RESET_ALL}")

def main():
    t_c = threading.Thread(target=consumer)
    t_p = threading.Thread(target=producer)
    t_c.start(); t_p.start()
    t_c.join(); t_p.join()

if __name__ == "__main__":
    main()

Run:

python cond_mini_example.py

🧩 Parallels Between C and Python Implementations

Concept C API Python Equivalent
Barrier pthread_barrier_t threading.Barrier
Mutex pthread_mutex_t threading.Lock() or Condition
Condition Variable pthread_cond_t threading.Condition()
Producer–Consumer Queue Manual + pthread_cond_wait queue.Queue(maxsize=N)
Thread pthread_create() threading.Thread()
Sleep / Wait sleep() / usleep() time.sleep()
Color Output ANSI codes colorama (cross-platform)

✅ Key Takeaways for Python

  • Threading concepts and patterns mirror those in C closely.
  • Python’s queue.Queue simplifies synchronization for many use cases.
  • Condition variables (threading.Condition) follow the same while + wait() rule.
  • colorama ensures colored output works on Windows, macOS, and Linux.
  • Both languages share the same fundamental synchronization ideas — Python just abstracts them more.
Previous
Next