C Language Multithreading Programming: From Beginner to Proficiency, Understand It Thoroughly in One Article

Follow and star our public account for direct access to exciting content

C Language Multithreading Programming: From Beginner to Proficiency, Understand It Thoroughly in One Article

Content

1. Basic Concepts of Multithreading

1. What is a Thread?

A thread is the smallest unit of execution in a program, representing an independent execution path within a process. A process can contain multiple threads that share the process’s memory space (such as global variables and heap memory), but each thread has its own stack space (used for storing local variables and function call information).

2. Difference Between Threads and Processes

Characteristic Process Thread
Memory Space Independent memory space Shares the process’s memory space
Resource Overhead High cost for creation and destruction Low cost for creation and destruction
Communication Method Requires inter-process communication (IPC) Directly shares memory, communication is more efficient
Context Switching High switching cost Low switching cost
Stability A process crash does not affect other processes A thread crash may cause the entire process to crash

3. Why Use Multithreading?

  • Improves program responsiveness: For example, in GUI programs, the main thread handles the interface while child threads handle time-consuming tasks.
  • Fully utilizes multi-core CPUs: Multithreading can execute in parallel, enhancing computational efficiency.
  • Simplifies the development of complex tasks: Break down tasks into multiple threads for clearer logic.

2. Implementing Multithreading in C (POSIX Thread Library)

1. Introduction to POSIX Thread Library

The C language does not directly support multithreading, but multithreading programming can be achieved through the POSIX Thread Library (pthread). This library is cross-platform, primarily used in Linux/Unix systems, while Windows requires the Win32 API (this article uses pthread as an example).

2. Basic Functions

Function Purpose
<span>pthread_create()</span> Create a thread
<span>pthread_join()</span> Wait for a thread to finish
<span>pthread_exit()</span> Terminate the current thread
<span>pthread_detach()</span> Set the thread to detached state (no need to wait)
<span>pthread_mutex_lock()</span> Lock (protect shared resources)
<span>pthread_mutex_unlock()</span> Unlock
<span>pthread_cond_wait()</span> Condition variable wait
<span>pthread_cond_signal()</span> Wake up waiting threads

3. Multithreading Programming Beginner Example

1. Creating a Thread

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

// Thread execution function
void* thread_function(void* arg) {
    int value = *(int*)arg;
    printf("Hello from thread! Value: %d\n", value);
    return NULL;
}

int main() {
    pthread_t thread;  // Define thread identifier
    int arg = 42;      // Parameter passed to the thread

    // Create thread
    if (pthread_create(&thread, NULL, thread_function, &arg) != 0) {
        perror("Failed to create thread");
        return 1;
    }

    // Wait for thread to finish
    pthread_join(thread, NULL);
    printf("Main thread finished.\n");

    return 0;
}

Compilation Command:

gcc -o thread_example thread_example.c -lpthread

Run Result:

Hello from thread! Value: 42
Main thread finished.
C Language Multithreading Programming: From Beginner to Proficiency, Understand It Thoroughly in One Article
image

2. Thread Lifecycle

  • Creation: <span>pthread_create()</span>
  • Running: The thread executes the specified function
  • Waiting: <span>pthread_join()</span> waits for the thread to finish
  • Detached: <span>pthread_detach()</span> releases thread resources
  • Termination: <span>pthread_exit()</span> or the thread function returns

4. Thread Synchronization Mechanisms

1. Shared Resources and Data Races

When multiple threads access shared resources (such as global variables) simultaneously, it may lead to data inconsistency. For example:

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

int counter = 0;

void* increment_counter(void* arg) {
    for (int i = 0; i < 100000; i++) {
        counter++;  // Unprotected shared resource
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, increment_counter, NULL);
    pthread_create(&t2, NULL, increment_counter, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("Final counter value: %d\n", counter);  // Expected value is 200000, but actual may be less
    return 0;
}
C Language Multithreading Programming: From Beginner to Proficiency, Understand It Thoroughly in One Article
image

Problem: <span>counter++</span> is a non-atomic operation (read-modify-write), and multiple threads operating simultaneously can lead to data races.

2. Mutex

A mutex is used to protect shared resources, ensuring that only one thread accesses them at a time.

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

int counter = 0;
pthread_mutex_t lock;  // Define mutex

void* increment_counter(void* arg) {
    for (int i = 0; i < 100000; i++) {
        pthread_mutex_lock(&lock);  // Lock
        counter++;
        pthread_mutex_unlock(&lock);  // Unlock
    }
    return NULL;
}

int main() {
    pthread_mutex_init(&lock, NULL);  // Initialize mutex

    pthread_t t1, t2;
    pthread_create(&t1, NULL, increment_counter, NULL);
    pthread_create(&t2, NULL, increment_counter, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    pthread_mutex_destroy(&lock);  // Destroy mutex
    printf("Final counter value: %d\n", counter);  // Output 200000
    return 0;
}
C Language Multithreading Programming: From Beginner to Proficiency, Understand It Thoroughly in One Article
image

3. Condition Variable

A condition variable is used for inter-thread communication, waking up waiting threads when a certain condition is met.

Example: Producer-Consumer Model

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

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE];
int count = 0;
pthread_mutex_t lock = 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 = 1; i <= 10; i++) {
        pthread_mutex_lock(&lock);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&not_full, &lock);  // Wait when buffer is full
        }
        buffer[count++] = i;
        printf("Produced: %d\n", i);
        pthread_cond_signal(&not_empty);  // Notify consumer
        pthread_mutex_unlock(&lock);
        usleep(100000);  // Simulate production time
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 1; i <= 10; i++) {
        pthread_mutex_lock(&lock);
        while (count == 0) {
            pthread_cond_wait(&not_empty, &lock);  // Wait when buffer is empty
        }
        int item = buffer[--count];
        printf("Consumed: %d\n", item);
        pthread_cond_signal(&not_full);  // Notify producer
        pthread_mutex_unlock(&lock);
        usleep(100000);  // Simulate consumption time
    }
    return NULL;
}

int main() {
    pthread_t p, c;
    pthread_create(&p, NULL, producer, NULL);
    pthread_create(&c, NULL, consumer, NULL);

    pthread_join(p, NULL);
    pthread_join(c, NULL);

    return 0;
}
C Language Multithreading Programming: From Beginner to Proficiency, Understand It Thoroughly in One Article
image

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

Follow my public WeChat account and reply "C Language" to receive 300G of programming materials for free.

Feel free to like, share, recommend, and leave comments.

Leave a Comment