087. 理解C语言中的并发编程

并发编程是指同时执行多个任务,这些任务可以是线程、进程或协程。在C语言中,并发编程通常通过多线程或多进程实现。并发编程可以显著提高程序的性能,尤其是在多核处理器上。然而,它也带来了同步和通信的挑战。

1. 并发编程的基本概念

1.1 线程(Threads)

线程是操作系统能够进行运算调度的最小单位。多个线程可以共享进程的资源,但每个线程有自己的执行栈和程序计数器。

1.2 进程(Processes)

进程是操作系统分配资源的基本单位。每个进程有自己的地址空间、内存、数据栈等。

1.3 协程(Coroutines)

协程是一种更轻量级的线程,通常由用户态管理,而不是由操作系统内核管理。C语言标准库中没有直接支持协程,但可以通过库(如libtask)实现。

2. 多线程编程

在C语言中,多线程编程通常通过POSIX线程(pthread)库实现。以下是一个简单的多线程示例。

示例代码:多线程

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

void* threadFunction(void* arg) {
    int* data = (int*)arg;
    printf("线程 %d 执行中...\n", *data);
    return NULL;
}

int main() {
    pthread_t threads[5];
    int thread_ids[5];

    for (int i = 0; i < 5; i++) {
        thread_ids[i] = i;
        if (pthread_create(&threads[i], NULL, threadFunction, &thread_ids[i]) != 0) {
            perror("线程创建失败");
            return 1;
        }
    }

    for (int i = 0; i < 5; i++) {
        if (pthread_join(threads[i], NULL) != 0) {
            perror("线程等待失败");
            return 1;
        }
    }

    return 0;
}

示例运行

输入:

无输入

输出:

线程 0 执行中...
线程 1 执行中...
线程 2 执行中...
线程 3 执行中...
线程 4 执行中...

3. 多进程编程

在C语言中,多进程编程通常通过fork函数实现。以下是一个简单的多进程示例。

示例代码:多进程

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

void processFunction(int pid) {
    printf("进程 %d 执行中...\n", pid);
}

int main() {
    pid_t pids[5];

    for (int i = 0; i < 5; i++) {
        pids[i] = fork();
        if (pids[i] < 0) {
            perror("进程创建失败");
            return 1;
        } else if (pids[i] == 0) {
            // 子进程
            processFunction(i);
            exit(0);
        }
    }

    for (int i = 0; i < 5; i++) {
        wait(NULL); // 等待子进程结束
    }

    return 0;
}

示例运行

输入:

无输入

输出:

进程 0 执行中...
进程 1 执行中...
进程 2 执行中...
进程 3 执行中...
进程 4 执行中...

4. 线程同步

在多线程编程中,同步是一个关键问题。C语言提供了多种同步机制,如互斥锁(mutex)、条件变量和读写锁。

示例代码:互斥锁

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int sharedVariable = 0;

void* incrementFunction(void* arg) {
    for (int i = 0; i < 100000; i++) {
        pthread_mutex_lock(&mutex);
        sharedVariable++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t threads[2];

    for (int i = 0; i < 2; i++) {
        if (pthread_create(&threads[i], NULL, incrementFunction, NULL) != 0) {
            perror("线程创建失败");
            return 1;
        }
    }

    for (int i = 0; i < 2; i++) {
        if (pthread_join(threads[i], NULL) != 0) {
            perror("线程等待失败");
            return 1;
        }
    }

    printf("共享变量的值: %d\n", sharedVariable);

    return 0;
}

示例运行

输入:

无输入

输出:

共享变量的值: 200000

5. 注意事项

线程安全

  • 使用互斥锁或其他同步机制保护共享资源,避免数据竞争。

线程同步

  • 使用条件变量或信号量来协调线程间的执行顺序。

线程销毁

  • 确保线程结束后释放相关资源,避免内存泄漏。

线程库的选择

  • POSIX线程(pthread)是类Unix系统中广泛使用的线程库,功能丰富。

  • C11线程支持是C语言标准的一部分,但支持的编译器可能较少。

多进程与多线程的选择

  • 多进程适用于需要独立地址空间的场景,但进程间通信成本较高。

  • 多线程适用于需要共享地址空间的场景,但线程同步和通信较为复杂。

6. 总结

通过使用多线程和多进程,可以在C语言中实现并发编程。多线程编程通过POSIX线程(pthread)库实现,而多进程编程通过fork函数实现。合理使用线程同步机制,如互斥锁和条件变量,可以避免数据竞争和线程安全问题。在实际开发中,应根据具体需求选择合适的并发模型。

视频讲解

BiliBili: 视睿网络-哔哩哔哩视频 (bilibili.com)