» C语言快速入门 » 2. 高级篇 » 2.2 并发

并发

在 C 语言中,通常使用线程来实现并发线程是进程内独立运行的执行流,它们共享相同的内存空间,使它们能够相互通信和协调。

线程

<pthread.h><threads.h>(C11标准引入)提供了线程相关函数。

#include <stdio.h>
#include <threads.h>

#define NUM_THREADS 2

// 线程要执行的函数
int printHello(void *arg) {
    long threadId = (long)arg;
    printf("Hello from thread %ld\n", threadId);
    return 0;
}

int main() {
    thrd_t threads[NUM_THREADS];

    // 创建线程
    for (long t = 0; t < NUM_THREADS; t++) {
        if (thrd_create(&threads[t], printHello, (void *)t) != thrd_success) {
            fprintf(stderr, "Error creating thread %ld\n", t);
            return 1;
        }
    }

    // 等待线程完成
    for (long t = 0; t < NUM_THREADS; t++) {
        int result;
        if (thrd_join(threads[t], &result) != thrd_success) {
            fprintf(stderr, "Error joining thread %ld\n", t);
            return 2;
        }
    }

    // 所有线程都已完成。
    printf("All threads have completed.\n");

    return 0;
}

Mutex 互斥锁

Mutex 互斥锁(“mutual exclusion” 的缩写)是在并发编程中用于确保一次只能有一个线程访问共享资源或代码临界区的同步机制。

使用互斥锁的目的是防止多个线程尝试同时修改共享数据时发生数据损坏和意外行为。

#include <stdio.h>
#include <stdlib.h>
#include <threads.h>

#define NUM_THREADS 4
#define ITERATIONS   100000

int sharedCounter = 0;
mtx_t mutex; // 保护 sharedCounter 的互斥锁

// 每个线程要执行的函数
int incrementCounter(void *arg) {
    for (int i = 0; i < ITERATIONS; i++) {
        mtx_lock(&mutex); // 在访问 sharedCounter 之前锁定互斥锁
        sharedCounter++;
        mtx_unlock(&mutex); // 修改 sharedCounter 后解锁互斥锁
    }

    return 0;
}

int main() {
    thrd_t threads[NUM_THREADS];

    // 初始化互斥锁
    if (mtx_init(&mutex, mtx_plain) != thrd_success) {
        fprintf(stderr, "Error initializing mutex.\n");
        return 1;
    }

    // 创建线程
    for (long t = 0; t < NUM_THREADS; t++) {
        if (thrd_create(&threads[t], incrementCounter, NULL) != thrd_success) {
            fprintf(stderr, "Error creating thread %ld\n", t);
            return 2;
        }
    }

    // 等待线程完成
    for (long t = 0; t < NUM_THREADS; t++) {
        if (thrd_join(threads[t], NULL) != thrd_success) {
            fprintf(stderr, "Error joining thread %ld\n", t);
            return 3;
        }
    }
    mtx_destroy(&mutex);
    printf("Shared counter: %d\n", sharedCounter);
    return 0;
}
  • mtx_t 类型表示互斥锁。
  • mtx_init 函数初始化互斥锁。
  • mtx_lock 函数在访问共享数据之前锁定互斥锁。
  • mtx_unlock 函数在修改共享数据后解锁互斥锁。
  • incrementCounter 函数用于在循环内增加共享计数器。

条件变量

条件变量是用于线程间通信的同步原语。它提供了一种方式用于阻塞线程,直到某个条件被满足。这个机制可允许线程之间协调行动。

cnd_t 类型表示条件变量,它是 <threads.h> 库的一部分。

#include <stdio.h>
#include <threads.h>

cnd_t condition;
mtx_t mutex;
int sharedValue = 0;

int main_thread(void *arg) {
    for (int i = 0; i < 5; ++i) {
        mtx_lock(&mutex);
        sharedValue = i;
        printf("Main thread: Set sharedValue to %d\n", sharedValue);
        cnd_broadcast(&condition);
        mtx_unlock(&mutex);
        thrd_yield(); // 让位给其他线程执行
    }

    return 0;
}

int worker_thread(void *arg) {
    for (int i = 0; i < 5; ++i) {
        mtx_lock(&mutex);

        // 等待条件信号
        while (sharedValue != i) {
            cnd_wait(&condition, &mutex);
        }

        printf("Worker thread: Received sharedValue %d\n", sharedValue);

        mtx_unlock(&mutex);
        thrd_yield(); // 让位给其他线程执行
    }

    return 0;
}

int main() {
    thrd_t mainThread, workerThread;

    // 初始化互斥锁和条件变量
    mtx_init(&mutex, mtx_plain);
    cnd_init(&condition);

    // 创建线程
    thrd_create(&mainThread, main_thread, NULL);
    thrd_create(&workerThread, worker_thread, NULL);

    // 等待线程完成
    thrd_join(mainThread, NULL);
    thrd_join(workerThread, NULL);

    // 销毁互斥锁和条件变量
    mtx_destroy(&mutex);
    cnd_destroy(&condition);

    return 0;
}

cnd_t 相关的函数如下:

  • int cnd_init(cnd_t *cond): 初始化条件变量。
  • int cnd_destroy(cnd_t *cond): 销毁条件变量。
  • int cnd_signal(cnd_t *cond): 向正在等待条件变量的一个线程发信号。如果没有线程在等待,信号不会产生任何效果。
  • int cnd_broadcast(cnd_t *cond): 向正在等待条件变量的所有线程发信号,会唤醒所有等待的线程。
  • int cnd_wait(cnd_t *cond, mtx_t *mtx): 释放关联的互斥锁(mtx)并等待条件变量。当条件被发信号或广播时,线程被唤醒并重新获取互斥锁。

代码挑战

编写一个模拟两个线程之间简单共享缓冲区的 C 程序。一个线程(Producer)向缓冲区添加项目,另一个线程(Consumer)从缓冲区中移除项目。 使用 mutex 来确保线程安全。

Loading...
> 此处输出代码运行结果
上页
下页