导读:本期聚焦于小伙伴创作的《Linux多线程编程怎么实现?从创建到同步的完整步骤解析》,敬请观看详情。为什么单进程程序在多核服务器上CPU利用率总跑不满?根本原因在于缺少并发执行单元。Linux下借助POSIX线程库pthread,可以把一个任务拆成多个轻量执行流。实现多线程首先要包含pthread.h并链接libpthread,用pthread_create拉起线程,线程函数需符合void*返回值与void*参数约定。但真正麻烦的是数据共享:多个线程同时改同一变量会触发竞态,必须靠互斥锁或条件变量约束执行顺序。本文以具体代码演示线程创建、参数传递、加锁保护临界区,以及用join回收资源的做法,帮你避开常见踩坑点。

在Linux环境中,多线程编程是提升程序并发能力的核心手段。通过在一个进程内创建多个执行流,可以充分利用多核CPU的计算资源,尤其适合处理高并发网络服务、批量数据处理等场景。POSIX线程(通常称为pthread)是Linux系统上最标准的线程实现,几乎所有发行版都自带了对应的开发库。

Linux多线程编程怎么实现?从创建到同步的完整步骤解析

一、线程的创建与基本结构

Linux多线程编程依赖POSIX线程库,头文件为<pthread.h>。编译时需要显式链接动态库,例如使用gcc时添加-lpthread参数。线程的入口函数有固定签名:返回值为void*,参数也为void*,这允许我们传递任意类型的指针数据。

使用pthread_create函数可以新建线程,其原型为int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)。第一个参数用于接收线程ID,第二个一般为空表示默认属性,第三个是线程函数,第四个是传入参数。下面是一段最简单的创建示例:

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

void* worker(void* arg) {
    int id = *(int*)arg;
    printf("线程 %d 正在运行n", id);
    return NULL;
}

int main() {
    pthread_t tid;
    int num = 1;
    pthread_create(&tid, NULL, worker, &num);
    pthread_join(tid, NULL);
    return 0;
}

上述代码中,主线程通过pthread_join等待子线程结束,否则进程可能提前退出导致线程来不及执行。在实际工程中,常把多个线程ID存入数组,循环创建与回收。

需要注意,传递给线程的参数如果是栈上局部变量,必须确保在线程使用期间该变量依然有效。更安全的做法是在堆上分配参数,或传递全局变量。此外,线程函数内部若需修改外部状态,要考虑后续提到的同步问题。

二、线程间的数据共享与竞态条件

同一进程内的线程天然共享地址空间,这意味着全局变量、堆内存对所有线程可见。这种共享特性带来便利,也引入了竞态条件:当多个线程未加保护地修改同一数据时,最终结果依赖调度顺序,往往产生错误数值。

例如两个线程各对全局计数器累加十万次,理论上结果应为二十万,但运行时经常得到更小的值。原因在于i++在汇编层面分为读取、加一、写回三步,线程可能在写回前被切换,导致彼此覆盖。下面的代码演示了这一问题:

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

int counter = 0;

void* add(void* arg) {
    for (int i = 0; i < 100000; i++) {
        counter++;
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, add, NULL);
    pthread_create(&t2, NULL, add, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("counter = %dn", counter);
    return 0;
}

运行这段程序,打印的counter经常小于200000。要解决这个问题,必须采用同步机制把“读-改-写”过程变成原子操作,或者让同一时间只有一个线程进入临界区。

三、使用互斥锁保护临界区

互斥锁(mutex)是最常用的线程同步工具。它如同洗手间的门锁:谁先进去就持有钥匙,出来后才轮到下一个。在pthread中,类型为pthread_mutex_t,使用前需初始化,可以用PTHREAD_MUTEX_INITIALIZER静态初始化,也可调用pthread_mutex_init

加锁与解锁分别对应pthread_mutex_lockpthread_mutex_unlock。把counter自增放在锁内,就能彻底消除竞态。改进代码如下:

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

int counter = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

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

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, safe_add, NULL);
    pthread_create(&t2, NULL, safe_add, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("counter = %dn", counter);
    pthread_mutex_destroy(&lock);
    return 0;
}

此时无论运行多少次,结果都稳定为200000。不过频繁加锁会带来微小性能开销,若临界区极短,可考虑将循环移入锁内以减少锁操作次数,但需权衡代码清晰度和并发度。

使用互斥锁要防止死锁:例如同一线程连续两次pthread_mutex_lock同一把非递归锁会阻塞自身。复杂系统中应按固定顺序获取多把锁,或改用读写锁、自旋锁等更合适的原语。

四、条件变量与线程协作

除了互斥锁,条件变量(condition variable)用于在线程间传递“某状态已改变”的信号。典型场景是生产者消费者模型:消费者在缓冲区空时等待,生产者放入数据后通知消费者。条件变量必须与互斥锁配合使用,类型为pthread_cond_t

等待方调用pthread_cond_wait,该函数会自动释放锁并休眠,被唤醒后重新获得锁;通知方调用pthread_cond_signalpthread_cond_broadcast。以下片段展示基础用法:

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

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
int ready = 0;

void* consumer(void* arg) {
    pthread_mutex_lock(&m);
    while (ready == 0) {
        pthread_cond_wait(&c, &m);
    }
    printf("收到通知,开始处理n");
    pthread_mutex_unlock(&m);
    return NULL;
}

void* producer(void* arg) {
    pthread_mutex_lock(&m);
    ready = 1;
    pthread_cond_signal(&c);
    pthread_mutex_unlock(&m);
    return NULL;
}

注意等待处用while而非if判断条件,这是因为存在虚假唤醒可能。条件变量不保存状态,只负责通知,真实条件仍需用户自己维护。

在真实服务开发中,条件变量常配合队列使用,避免忙等待浪费CPU。它和互斥锁组合,构成了大多数线程池、任务调度框架的底层支撑。

五、线程资源回收与取消

线程结束后的资源回收有两种方式:一是主线程调用pthread_join阻塞等待并获取退出值;二是设置分离属性pthread_detach,让线程结束后自动释放资源,无需他人回收。若既不join也不detach,线程会变成僵尸线程,占用少量内核结构。

有时需要主动终止线程,可调用pthread_cancel向目标线程发送取消请求,但对方可能在不可取消点停留。更推荐的做法是设置标志位,让线程函数在合适位置自行退出,这样能安全释放锁和内存。

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

volatile bool running = true;

void* loop_task(void* arg) {
    while (running) {
        // 执行周期工作
    }
    printf("线程干净退出n");
    return NULL;
}

通过共享running变量,外部将其置为false即可温和停止线程。相比强制取消,这种协作式退出能避免破坏数据一致性。

总体来看,Linux多线程编程的实现路径十分清晰:引入pthread库、创建线程、通过互斥锁与条件变量管理共享数据、最后妥善回收。掌握这些基础组件,就能构建出稳定高效的并发程序。

Linux多线程pthread线程同步修改时间:2026-08-01 15:27:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。