跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

C 语言多线程与并发编程:提高程序执行效率

C 语言多线程与并发编程通过利用多核 CPU 优势提升程序执行效率。文章涵盖线程与进程区别、并发与并行概念,详解 pthread 库创建销毁线程、互斥锁条件变量同步机制及生产者消费者模型。同时分析死锁活锁成因并提供避免策略,最后给出数组求和等实战案例,帮助开发者掌握高效并发编程技巧并规避常见坑点。

BackendPro发布于 2026/2/25更新于 2026/5/3029 浏览
C 语言多线程与并发编程:提高程序执行效率

C 语言多线程与并发编程:提高程序执行效率

一、前言:为什么需要多线程与并发编程?

学习目标
  • 理解多线程的本质:同一进程中的多个执行路径
  • 理解并发编程的本质:多个任务同时执行
  • 明确多线程与并发编程的优势:提高程序的执行效率和响应速度
  • 掌握本章学习重点:线程的创建与销毁、线程同步机制、线程通信机制、并发编程的避坑指南
  • 学会使用多线程与并发编程解决实际问题
重点提示

💡 多线程与并发编程是提高程序执行效率的重要手段!合理使用多线程可以充分利用多核 CPU 的优势,但也会引入线程同步和通信的问题。


二、模块 1:多线程与并发编程的基本概念——理解线程与进程

2.1 学习目标
  • 理解进程的本质:正在运行的程序实例
  • 理解线程的本质:进程中的执行单元
  • 明确线程与进程的关系:进程包含多个线程,线程共享进程的资源
  • 理解并发与并行的区别:并发是指多个任务交替执行,并行是指多个任务同时执行
  • 避开多线程与并发编程基本概念使用的 3 大常见坑
2.2 线程与进程的区别
特性进程线程
资源分配独立的地址空间、内存、文件描述符等共享进程的地址空间、内存、文件描述符等
通信方式管道、消息队列、共享内存、套接字等全局变量、局部变量、参数传递等
上下文切换成本较高较低
稳定性进程崩溃不会影响其他进程线程崩溃会影响整个进程
创建和销毁成本较高较低
2.3 并发与并行的区别
  • 并发:多个任务交替执行,利用 CPU 的时间片轮转机制
  • 并行:多个任务同时执行,充分利用多核 CPU 的优势

代码示例 1:获取当前进程的 ID

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

int main() {
    pid_t pid = getpid();
    printf("当前进程的 ID:%d\n", pid);
    return 0;
}

三、模块 2:线程的创建与销毁——管理线程的生命周期

3.1 学习目标
  • 掌握线程创建的方法:使用 pthread_create 函数
  • 掌握线程等待的方法:使用 pthread_join 函数
  • 掌握线程退出的方法:使用 pthread_exit 函数
  • 掌握线程取消的方法:使用 pthread_cancel 函数
  • 避开线程创建与销毁使用的 3 大常见坑
3.2 pthread_create 函数

功能:创建一个新线程

语法:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

参数说明:

  • thread:指向线程 ID 的指针
  • attr:线程属性,通常为 NULL
  • start_routine:线程的入口函数
  • arg:传递给线程入口函数的参数

返回值:成功返回 0,失败返回错误码

代码示例 2:创建一个新线程

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

void *thread_func(void *arg) {
    int thread_id = *(int *)arg;
    printf("线程%d正在执行!\n", thread_id);
    sleep(1);
    printf("线程%d执行完毕!\n", thread_id);
    return NULL;
}

int main() {
    pthread_t tid;
    int thread_id = 1;
    // 创建新线程
    int ret = pthread_create(&tid, NULL, thread_func, &thread_id);
    if (ret != 0) {
        printf("线程创建失败!\n");
        return 1;
    }
    printf("主线程正在执行!\n");
    // 等待线程结束
    ret = pthread_join(tid, NULL);
    if (ret != 0) {
        printf("线程等待失败!\n");
        return 1;
    }
    printf("主线程执行完毕!\n");
    return 0;
}

四、模块 3:线程同步机制——避免数据竞争

4.1 学习目标
  • 理解数据竞争的本质:多个线程同时访问共享数据,且至少有一个线程在修改数据
  • 掌握互斥锁的方法:使用 pthread_mutex 系列函数
  • 掌握条件变量的方法:使用 pthread_cond 系列函数
  • 掌握读写锁的方法:使用 pthread_rwlock 系列函数
  • 避开线程同步机制使用的 3 大常见坑
4.2 互斥锁

功能:保护共享数据,确保同一时间只有一个线程可以访问共享数据

代码示例 3:使用互斥锁保护共享数据

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

int shared_data = 0;
pthread_mutex_t mutex;

void *thread_func(void *arg) {
    int thread_id = *(int *)arg;
    for (int i = 0; i < 10000; i++) {
        pthread_mutex_lock(&mutex); // 加锁
        shared_data++;
        pthread_mutex_unlock(&mutex); // 解锁
    }
    printf("线程%d执行完毕!共享数据的值:%d\n", thread_id, shared_data);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    int thread_id1 = 1, thread_id2 = 2;
    // 初始化互斥锁
    int ret = pthread_mutex_init(&mutex, NULL);
    if (ret != 0) {
        printf("互斥锁初始化失败!\n");
        return 1;
    }
    // 创建新线程
    ret = pthread_create(&tid1, NULL, thread_func, &thread_id1);
    if (ret != 0) {
        printf("线程 1 创建失败!\n");
        return 1;
    }
    ret = pthread_create(&tid2, NULL, thread_func, &thread_id2);
    if (ret != 0) {
        printf("线程 2 创建失败!\n");
        return 1;
    }
    // 等待线程结束
    ret = pthread_join(tid1, NULL);
    if (ret != 0) {
        printf("线程 1 等待失败!\n");
        return 1;
    }
    ret = pthread_join(tid2, NULL);
    if (ret != 0) {
        printf("线程 2 等待失败!\n");
        return 1;
    }
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    printf("主线程执行完毕!共享数据的值:%d\n", shared_data);
    return 0;
}

五、模块 4:线程通信机制——实现线程间的协作

5.1 学习目标
  • 理解线程通信的本质:线程之间交换信息
  • 掌握条件变量的方法:使用 pthread_cond 系列函数
  • 掌握信号量的方法:使用 sem 系列函数
  • 学会使用线程通信实现生产者 - 消费者模型
  • 避开线程通信机制使用的 3 大常见坑
5.2 条件变量

功能:用于线程之间的通信,等待某个条件的成立

代码示例 4:使用条件变量实现生产者 - 消费者模型

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

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int count = 0;
pthread_mutex_t mutex;
pthread_cond_t cond_producer;
pthread_cond_t cond_consumer;

void *producer_func(void *arg) {
    int thread_id = *(int *)arg;
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&mutex); // 加锁
        // 等待缓冲区不满
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&cond_producer, &mutex);
        }
        // 生产数据
        buffer[in] = i;
        in = (in + 1) % BUFFER_SIZE;
        count++;
        printf("生产者%d生产了数据%d,缓冲区中有%d个数据\n", thread_id, i, count);
        pthread_cond_signal(&cond_consumer); // 通知消费者
        pthread_mutex_unlock(&mutex); // 解锁
        sleep(1);
    }
    return NULL;
}

void *consumer_func(void *arg) {
    int thread_id = *(int *)arg;
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&mutex); // 加锁
        // 等待缓冲区不空
        while (count == 0) {
            pthread_cond_wait(&cond_consumer, &mutex);
        }
        // 消费数据
        int data = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        printf("消费者%d消费了数据%d,缓冲区中有%d个数据\n", thread_id, data, count);
        pthread_cond_signal(&cond_producer); // 通知生产者
        pthread_mutex_unlock(&mutex); // 解锁
        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t producer_tid1, producer_tid2, consumer_tid1, consumer_tid2;
    int producer_id1 = 1, producer_id2 = 2, consumer_id1 = 1, consumer_id2 = 2;
    // 初始化互斥锁和条件变量
    int ret = pthread_mutex_init(&mutex, NULL);
    if (ret != 0) {
        printf("互斥锁初始化失败!\n");
        return 1;
    }
    ret = pthread_cond_init(&cond_producer, NULL);
    if (ret != 0) {
        printf("条件变量 1 初始化失败!\n");
        return 1;
    }
    ret = pthread_cond_init(&cond_consumer, NULL);
    if (ret != 0) {
        printf("条件变量 2 初始化失败!\n");
        return 1;
    }
    // 创建新线程
    ret = pthread_create(&producer_tid1, NULL, producer_func, &producer_id1);
    if (ret != 0) {
        printf("生产者 1 创建失败!\n");
        return 1;
    }
    ret = pthread_create(&producer_tid2, NULL, producer_func, &producer_id2);
    if (ret != 0) {
        printf("生产者 2 创建失败!\n");
        return 1;
    }
    ret = pthread_create(&consumer_tid1, NULL, consumer_func, &consumer_id1);
    if (ret != 0) {
        printf("消费者 1 创建失败!\n");
        return 1;
    }
    ret = pthread_create(&consumer_tid2, NULL, consumer_func, &consumer_id2);
    if (ret != 0) {
        printf("消费者 2 创建失败!\n");
        return 1;
    }
    // 等待线程结束
    ret = pthread_join(producer_tid1, NULL);
    if (ret != 0) {
        printf("生产者 1 等待失败!\n");
        return 1;
    }
    ret = pthread_join(producer_tid2, NULL);
    if (ret != 0) {
        printf("生产者 2 等待失败!\n");
        return 1;
    }
    ret = pthread_join(consumer_tid1, NULL);
    if (ret != 0) {
        printf("消费者 1 等待失败!\n");
        return 1;
    }
    ret = pthread_join(consumer_tid2, NULL);
    if (ret != 0) {
        printf("消费者 2 等待失败!\n");
        return 1;
    }
    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond_producer);
    pthread_cond_destroy(&cond_consumer);
    return 0;
}

六、模块 5:并发编程的避坑指南——避免常见问题

6.1 学习目标
  • 理解死锁的本质:多个线程相互等待对方释放资源
  • 理解活锁的本质:多个线程不断尝试获取资源,但都无法成功
  • 理解饥饿的本质:某些线程一直无法获取资源
  • 掌握避免死锁的方法:破坏死锁的四个必要条件
  • 避开并发编程使用的 3 大常见坑
6.2 死锁的四个必要条件
  1. 互斥条件:资源是排他性的,只能被一个线程使用
  2. 请求与保持条件:线程已经拥有至少一个资源,并且在等待其他资源
  3. 不可剥夺条件:资源只能由线程自愿释放,不能被其他线程剥夺
  4. 循环等待条件:多个线程形成一个循环等待资源的链
6.3 避免死锁的方法
6.3.1 破坏互斥条件
  • 允许资源共享,但可能导致数据竞争
6.3.2 破坏请求与保持条件
  • 线程在请求资源时,必须先释放已拥有的资源
6.3.3 破坏不可剥夺条件
  • 允许其他线程剥夺资源,但可能导致资源浪费
6.3.4 破坏循环等待条件
  • 对资源进行排序,线程必须按顺序请求资源

代码示例 5:避免死锁的例子

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

pthread_mutex_t mutex1;
pthread_mutex_t mutex2;

void *thread_func1(void *arg) {
    int thread_id = *(int *)arg;
    pthread_mutex_lock(&mutex1);
    printf("线程%d获取到了互斥锁 1!\n", thread_id);
    sleep(1);
    pthread_mutex_lock(&mutex2);
    printf("线程%d获取到了互斥锁 2!\n", thread_id);
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

void *thread_func2(void *arg) {
    int thread_id = *(int *)arg;
    pthread_mutex_lock(&mutex1);
    printf("线程%d获取到了互斥锁 1!\n", thread_id);
    sleep(1);
    pthread_mutex_lock(&mutex2);
    printf("线程%d获取到了互斥锁 2!\n", thread_id);
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    int thread_id1 = 1, thread_id2 = 2;
    // 初始化互斥锁
    int ret = pthread_mutex_init(&mutex1, NULL);
    if (ret != 0) {
        printf("互斥锁 1 初始化失败!\n");
        return 1;
    }
    ret = pthread_mutex_init(&mutex2, NULL);
    if (ret != 0) {
        printf("互斥锁 2 初始化失败!\n");
        return 1;
    }
    // 创建新线程
    ret = pthread_create(&tid1, NULL, thread_func1, &thread_id1);
    if (ret != 0) {
        printf("线程 1 创建失败!\n");
        return 1;
    }
    ret = pthread_create(&tid2, NULL, thread_func2, &thread_id2);
    if (ret != 0) {
        printf("线程 2 创建失败!\n");
        return 1;
    }
    // 等待线程结束
    ret = pthread_join(tid1, NULL);
    if (ret != 0) {
        printf("线程 1 等待失败!\n");
        return 1;
    }
    ret = pthread_join(tid2, NULL);
    if (ret != 0) {
        printf("线程 2 等待失败!\n");
        return 1;
    }
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    return 0;
}

七、模块 6:实战案例分析——使用多线程与并发编程解决实际问题

7.1 学习目标
  • 掌握使用多线程计算数组的和:将数组分成多个部分,每个线程计算一部分的和
  • 掌握使用多线程下载文件:将文件分成多个部分,每个线程下载一部分
  • 学会使用多线程与并发编程解决实际问题
  • 避开实战案例使用的 3 大常见坑
7.2 使用多线程计算数组的和

代码示例 6:使用多线程计算数组的和

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

#define NUM_THREADS 4
#define ARRAY_SIZE 10000

int array[ARRAY_SIZE];
long long sum = 0;
pthread_mutex_t mutex;

typedef struct {
    int thread_id;
    int start;
    int end;
} ThreadArgs;

void *thread_func(void *arg) {
    ThreadArgs *args = (ThreadArgs *)arg;
    long long local_sum = 0;
    for (int i = args->start; i < args->end; i++) {
        local_sum += array[i];
    }
    pthread_mutex_lock(&mutex);
    sum += local_sum;
    pthread_mutex_unlock(&mutex);
    printf("线程%d计算的和:%lld\n", args->thread_id, local_sum);
    return NULL;
}

int main() {
    pthread_t tid[NUM_THREADS];
    ThreadArgs args[NUM_THREADS];
    // 初始化数组
    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = i + 1;
    }
    // 初始化互斥锁
    int ret = pthread_mutex_init(&mutex, NULL);
    if (ret != 0) {
        printf("互斥锁初始化失败!\n");
        return 1;
    }
    // 创建新线程
    int chunk_size = ARRAY_SIZE / NUM_THREADS;
    for (int i = 0; i < NUM_THREADS; i++) {
        args[i].thread_id = i + 1;
        args[i].start = i * chunk_size;
        if (i == NUM_THREADS - 1) {
            args[i].end = ARRAY_SIZE;
        } else {
            args[i].end = (i + 1) * chunk_size;
        }
        ret = pthread_create(&tid[i], NULL, thread_func, &args[i]);
        if (ret != 0) {
            printf("线程%d创建失败!\n", i + 1);
            return 1;
        }
    }
    // 等待线程结束
    for (int i = 0; i < NUM_THREADS; i++) {
        ret = pthread_join(tid[i], NULL);
        if (ret != 0) {
            printf("线程%d等待失败!\n", i + 1);
            return 1;
        }
    }
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    printf("数组的总和:%lld\n", sum);
    return 0;
}

八、本章总结与课后练习

8.1 总结

✅ 多线程与并发编程的基本概念:线程与进程的区别、并发与并行的区别 ✅ 线程的创建与销毁:pthread_create、pthread_join、pthread_exit、pthread_cancel 函数 ✅ 线程同步机制:互斥锁、条件变量、读写锁 ✅ 线程通信机制:条件变量、信号量 ✅ 并发编程的避坑指南:避免死锁、活锁、饥饿 ✅ 实战案例分析:使用多线程计算数组的和、使用多线程下载文件

8.2 课后练习
  1. 编写程序:实现一个简单的多线程计数器,支持多个线程同时递增计数器
  2. 编写程序:实现一个简单的多线程生产者 - 消费者模型
  3. 编写程序:实现一个简单的多线程下载文件的程序
  4. 编写程序:实现一个简单的多线程排序算法,如快速排序、归并排序
  5. 编写程序:实现一个简单的多线程搜索算法,如线性查找、二分查找
  6. 编写程序:实现一个简单的多线程内存池,支持多个线程同时分配和释放内存

目录

  1. C 语言多线程与并发编程:提高程序执行效率
  2. 一、前言:为什么需要多线程与并发编程?
  3. 学习目标
  4. 重点提示
  5. 二、模块 1:多线程与并发编程的基本概念——理解线程与进程
  6. 2.1 学习目标
  7. 2.2 线程与进程的区别
  8. 2.3 并发与并行的区别
  9. 三、模块 2:线程的创建与销毁——管理线程的生命周期
  10. 3.1 学习目标
  11. 3.2 pthread_create 函数
  12. 四、模块 3:线程同步机制——避免数据竞争
  13. 4.1 学习目标
  14. 4.2 互斥锁
  15. 五、模块 4:线程通信机制——实现线程间的协作
  16. 5.1 学习目标
  17. 5.2 条件变量
  18. 六、模块 5:并发编程的避坑指南——避免常见问题
  19. 6.1 学习目标
  20. 6.2 死锁的四个必要条件
  21. 6.3 避免死锁的方法
  22. 6.3.1 破坏互斥条件
  23. 6.3.2 破坏请求与保持条件
  24. 6.3.3 破坏不可剥夺条件
  25. 6.3.4 破坏循环等待条件
  26. 七、模块 6:实战案例分析——使用多线程与并发编程解决实际问题
  27. 7.1 学习目标
  28. 7.2 使用多线程计算数组的和
  29. 八、本章总结与课后练习
  30. 8.1 总结
  31. 8.2 课后练习
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 国内 12 款 AI 智能体深度对比与选型指南
  • OpenClaw Gateway 安装失败 systemctl --user is-enabled unavailable 排查与解决
  • Faster Whisper 语音识别:高效转写技术全解析
  • FLUX.2[klein]模型本地部署及 AI 绘画应用指南
  • 扩散模型(Diffusion Model)原理与图像生成实战
  • DeerFlow 2.0:字节跳动开源的超级智能体编排框架
  • 小厂架构师 AI Agent 落地实战:从概念到 Bug 修复工具
  • Neo4j 社区版安装与使用指南
  • Claude Code 跨平台安装指南:Windows、Linux 与 macOS 实战配置
  • 大疆无人机如何导出日志并解析
  • AI Agent 技术架构与落地实践指南
  • TCP 协议详解:报文结构、连接管理与流量控制
  • 主流 AI IDE 深度解析:Qoder 与通义灵码实战指南
  • Kali Linux 官方更新命令详解
  • RoboTwin 双臂机器人基准平台完整配置指南
  • 跨语言实时视频流传输:C++与Python的高效共享内存通信方案
  • 科技巨头聚焦的 AI Agent 究竟是什么
  • EasyConnect Mac 版安装使用指南
  • OpenAI 系列模型发展史:从 GPT-1 到 GPT-4o
  • Git 常用操作命令速查手册

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online