---
# System prepended metadata

title: Recitation 9

---

# Multitasking and Multithreading in C

## Introduction

Multitasking and multithreading are programming techniques that enable a program to perform multiple tasks concurrently. In the C language, you can achieve multitasking through multithreading using the POSIX threads library (`pthread`).

## Multithreading in C using pthreads

### Step 1: Include the pthreads library

```c=
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
```

### Step 2: Create a thread function

```c=
void *thread_function(void *arg) {
    // Thread logic goes here
    printf("Hello from the thread!\n");
    return NULL;
}
```

- Thread Function (thread_function):

This is where you define the task that the new thread will perform. It must match the signature expected by pthread_create, which is a function pointer to void* (*)(void*).

### Step 3: Create and run threads

```c=
int main() {
    pthread_t thread_id;
    int result;

    // Create a thread
    result = pthread_create(&thread_id, NULL, thread_function, NULL);

    if (result != 0) {
        perror("Thread creation failed");
        return 1;
    }

    // Wait for the thread to finish
    pthread_join(thread_id, NULL);

    return 0;
}
```
- pthread_create() parameters

- pthread_t *thread: A pointer to pthread_t, which is an identifier for the new thread. After pthread_create successfully completes, this will hold the ID of the created thread.
- const pthread_attr_t *attr: A pointer to pthread_attr_t which specifies attributes for the thread. If this parameter is NULL, the thread is created with default attributes.
- void *(*start_routine) (void *): A pointer to the function to be executed by the thread. This function should take a single void * argument and return a void * value.
- void *arg: A pointer to the argument that will be passed to the start_routine function.

- Returns 0 on successful thread creation.
## Coordination between Threads

### Synchronization with Mutex

```c=
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int shared_data = 0;

void *thread_function(void *arg) {
    // Lock the mutex before accessing shared data
    pthread_mutex_lock(&mutex);

    // Access and modify shared data
    shared_data++;
    printf("Thread: Incremented shared_data to %d\n", shared_data);

    // Unlock the mutex when done
    pthread_mutex_unlock(&mutex);

    return NULL;
}

int main() {
    pthread_t thread_id;
    int result;

    // Create a thread
    result = pthread_create(&thread_id, NULL, thread_function, NULL);

    if (result != 0) {
        perror("Thread creation failed");
        return 1;
    }

    // Wait for the thread to finish
    pthread_join(thread_id, NULL);

    return 0;
}
```
- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

initializes the mutex to a default state. This is a static way of initializing a mutex. It's set up before the threads are created and ensures that the mutex is available for use as soon as the program starts.

- Locking the Mutex: pthread_mutex_lock(&mutex); 

This function is called before accessing the shared data. This ensures that only one thread can modify shared_data at a time. If the mutex is already locked by another thread, the calling thread will block until the mutex becomes available.


### Thread Communication with Condition Variables

```c=
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
int shared_data = 0;

void *producer_function(void *arg) {
    // Lock the mutex before modifying shared data
    pthread_mutex_lock(&mutex);

    // Produce data
    shared_data++;
    printf("Producer: Produced data, shared_data = %d\n", shared_data);

    // Signal the consumer that new data is available
    pthread_cond_signal(&condition);

    // Unlock the mutex
    pthread_mutex_unlock(&mutex);

    return NULL;
}

void *consumer_function(void *arg) {
    // Lock the mutex before checking shared data
    pthread_mutex_lock(&mutex);

    // Wait for new data to be produced
    while (shared_data == 0) {
        pthread_cond_wait(&condition, &mutex);
    }

    // Consume data
    shared_data--;
    printf("Consumer: Consumed data, shared_data = %d\n", shared_data);

    // Unlock the mutex
    pthread_mutex_unlock(&mutex);

    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;
    int result;

    // Create producer thread
    result = pthread_create(&producer_thread, NULL, producer_function, NULL);
    if (result != 0) {
        perror("Producer thread creation failed");
        return 1;
    }

    // Create consumer thread
    result = pthread_create(&consumer_thread, NULL, consumer_function, NULL);
    if (result != 0) {
        perror("Consumer thread creation failed");
        return 1;
    }

    // Wait for threads to finish
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    return 0;
}
```
- pthread_cond_wait

pthread_cond_t *cond: A pointer to the condition variable.
pthread_mutex_t *mutex: A pointer to the mutex associated with the condition variable.

pthread_cond_wait performs several actions atomically:
1. Release the Mutex: It releases the mutex pointed to by *mutex. This allows other threads to acquire the mutex and make changes to the shared state.
2. Wait for the Condition: The calling thread is blocked and waits for the condition variable *cond to be signaled. This wait is typically in response to some shared state reaching a particular condition.
4. Re-acquire the Mutex: When the condition variable is signaled (using pthread_cond_signal or pthread_cond_broadcast), the waiting thread wakes up and automatically re-acquires the mutex. It then continues execution after the pthread_cond_wait call.

