当前位置: 首页 > news >正文

C语言中的POSIX线程与多线程编程:从入门到实践

C语言中的POSIX线程与多线程编程:从入门到实践

在现代软件开发中,多线程编程已成为提升程序性能和响应速度的关键技术之一。C语言作为一种经典的编程语言,提供了多种方式来实现多线程编程,其中 POSIX 线程库(pthreads)是最常用的实现方式之一。本文将深入探讨 C 语言中的 POSIX 线程及多线程编程,帮助初学者理解其基本概念、常用函数和同步机制。 (【C 言专栏】C 语言中的多线程编程 - 阿里云开发者社区)


一、什么是 POSIX 线程?

POSIX 线程(POSIX Threads),简称 pthreads,是 POSIX 标准定义的一套线程接口。它为多线程编程提供了统一的 API,使得程序能够在类 Unix 系统(如 Linux、macOS 等)上创建、管理和同步线程。在 Windows 系统上,也可以通过第三方库(如 pthreads-w32)实现 pthreads 接口。 (线程, Pthreads)


二、POSIX 线程的基本组成

POSIX 线程库主要由以下几个部分组成:

  • 线程管理:创建、终止、等待线程等。
  • 同步原语:互斥锁(mutex)、条件变量(condition variable)、信号量(semaphore)等。
  • 线程属性:设置线程的栈大小、调度策略、分离状态等。
  • 线程局部存储:为每个线程分配独立的数据。

三、常用头文件与函数

1. 头文件

在使用 POSIX 线程时,需要包含以下头文件:

#include <pthread.h>  // POSIX 线程库
#include <semaphore.h> // POSIX 信号量库
#include <unistd.h>    // 提供 sleep 等系统调用

2. 线程管理函数

创建线程

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

  • thread: 输出型参数,用于接收新创建线程的标识符。通过该指针可操作或等待目标线程。
  • attr: 可选参数,指向pthread_attr_t结构体的指针,用于设置线程属性(如栈大小、调度策略等)。若为NULL则使用默认属性。
  • start_routine: 线程入口函数指针,新线程启动后将执行该函数。函数需符合void *func(void *)的签名规范。
  • arg: 传递给线程入口函数的参数指针,允许通过该参数向新线程传递上下文数据。
等待线程结束

pthread_join(pthread_t thread, void **retval)

  • thread: 输入参数,指定需等待的目标线程标识符。调用线程将阻塞直至该线程终止。
  • retval: 输出型参数,用于接收目标线程的退出状态。若线程通过pthread_exit返回数据,可通过该指针获取。
退出线程

pthread_exit(void *retval)

  • retval: 线程退出时的返回值指针。该值可通过pthread_join由其他线程获取,用于传递线程执行结果或状态。
获取当前线程 ID

pthread_self(void)

  • 无参数:直接返回调用线程的标识符(pthread_t类型),用于在无明确线程句柄时获取自身标识。

3. 互斥锁函数

初始化互斥锁

pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)

  • mutex: 输出型参数,指向需初始化的互斥锁对象。
  • attr: 可选参数,指向互斥锁属性对象的指针,用于配置锁类型(如普通锁、递归锁等)。若为NULL则使用默认属性。
加锁

pthread_mutex_lock(pthread_mutex_t *mutex)

  • mutex: 输入参数,指向已初始化的互斥锁。调用线程将阻塞直至成功获取锁所有权,用于保护临界区资源。
解锁

pthread_mutex_unlock(pthread_mutex_t *mutex)

  • mutex: 输入参数,指向需释放的互斥锁。仅持有锁的线程可执行此操作,释放后其他线程可竞争获取锁。
销毁互斥锁

pthread_mutex_destroy(pthread_mutex_t *mutex)

  • mutex: 输入参数,指向需销毁的互斥锁对象。销毁前需确保锁未被任何线程持有。

4. 条件变量函数

初始化条件变量

pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)

  • cond: 输出型参数,指向需初始化的条件变量对象。
  • attr: 可选参数,指向条件变量属性对象的指针,用于配置进程共享等属性。若为NULL则使用默认属性。
等待条件变量

pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)

  • cond: 输入参数,指向条件变量。调用线程将阻塞直至收到该条件的信号。
  • mutex: 输入参数,关联的互斥锁。此函数会自动释放锁并进入等待,被唤醒后重新获取锁,确保原子性操作。
发送信号

pthread_cond_signal(pthread_cond_t *cond)

  • cond: 输入参数,指向需发送信号的条件变量。唤醒至少一个等待该条件的线程。
销毁条件变量

pthread_cond_destroy(pthread_cond_t *cond)

  • cond: 输入参数,指向需销毁的条件变量对象。销毁前需确保无线程正在等待该条件。

5. 信号量函数

初始化信号量

sem_init(sem_t *sem, int pshared, unsigned int value)

  • sem: 输出型参数,指向需初始化的信号量对象。
  • pshared: 共享标志,0表示线程间共享,非0值表示进程间共享(需系统支持)。
  • value: 信号量初始计数值,表示可用资源数量。
等待信号量

sem_wait(sem_t *sem)

  • sem: 输入参数,指向信号量。若信号量计数值>0则减1并返回;若=0则阻塞直至计数值>0。
释放信号量

sem_post(sem_t *sem)

  • sem: 输入参数,指向信号量。将信号量计数值加1,并唤醒等待该信号量的线程(若有)。
销毁信号量

sem_destroy(sem_t *sem)

  • sem: 输入参数,指向需销毁的信号量对象。销毁前需确保无线程正在操作该信号量。

四、线程同步与互斥

在多线程编程中,多个线程可能会同时访问共享资源,导致数据竞争和不一致。为了解决这个问题,POSIX 线程库提供了多种同步机制:

1. 互斥锁(mutex)

互斥锁用于保护共享资源,确保同一时刻只有一个线程可以访问。

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_lock(&mutex);
// 访问共享资源
pthread_mutex_unlock(&mutex);

2. 条件变量(condition variable)

条件变量用于线程间的通信,允许线程在某些条件满足时被唤醒。

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_lock(&mutex);
while (/* 条件不满足 */) {pthread_cond_wait(&cond, &mutex);
}
// 访问共享资源
pthread_mutex_unlock(&mutex);

3. 信号量(semaphore)

信号量是一种同步原语,用于控制多个线程对共享资源的访问。

sem_t sem;
sem_init(&sem, 0, 1);sem_wait(&sem);
// 访问共享资源
sem_post(&sem);sem_destroy(&sem);

五、线程属性与管理

1. 线程属性(pthread_attr_t)

线程属性用于设置线程的栈大小、调度策略、分离状态等。

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 设置其他属性
pthread_create(&thread, &attr, thread_func, NULL);
pthread_attr_destroy(&attr);

2. 线程分离与连接

  • 分离线程pthread_detach(pthread_t thread);
  • 连接线程pthread_join(pthread_t thread, void **retval);

分离线程后,线程结束时会自动释放资源;连接线程用于等待线程结束并获取返回值。


六、完整示例:创建并同步两个线程

以下是一个简单的示例,演示如何创建两个线程并使用互斥锁进行同步:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>  // 引入 intptr_t 类型pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *thread_func(void *arg) {intptr_t thread_id = (intptr_t)arg;  // 使用 intptr_t 类型pthread_mutex_lock(&mutex);printf("线程 %ld 正在执行...\n", (long)thread_id);pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t threads[2];for (intptr_t i = 0; i < 2; i++) {if (pthread_create(&threads[i], NULL, thread_func, (void *)i) != 0) {perror("创建线程失败");exit(EXIT_FAILURE);}}for (int i = 0; i < 2; i++) {if (pthread_join(threads[i], NULL) != 0) {perror("等待线程失败");exit(EXIT_FAILURE);}}pthread_mutex_destroy(&mutex);return 0;
}

程序分析

在代码中,主线程创建了两个子线程,并等待它们完成。每个子线程在执行时会获取互斥锁(pthread_mutex_lock),打印一条消息,然后释放互斥锁(pthread_mutex_unlock)。由于互斥锁的存在,确保了在同一时刻只有一个线程能够执行打印操作,从而避免了输出的混乱。

然而,线程的执行顺序是由操作系统的调度器决定的,通常是基于时间片轮转或其他调度策略。因此,两个线程的执行顺序可能会有所不同,导致输出的顺序也不同。

可能的输出示例

  1. 线程 0 先执行:

    线程 0 正在执行...
    线程 1 正在执行...
    
  2. 线程 1 先执行:

    线程 1 正在执行...
    线程 0 正在执行...
    

在这个示例中,主线程创建了两个子线程,每个子线程在执行时会获取互斥锁,确保同一时刻只有一个线程在执行临界区代码。


七、总结

POSIX 线程库为 C 语言程序提供了强大的多线程支持,使得程序能够在类 Unix 系统上实现并发和并行处理。通过合理使用线程管理函数和同步机制,可以有效地提高程序的性能和响应速度。然而,多线程编程也带来了线程同步、死锁等挑战,需要开发者在设计和实现时特别注意。 (【C 言专栏】C 语言中的多线程编程 - 阿里云开发者社区, 基本功| 一文讲清多线程和多线程同步原创 - CSDN博客)

希望本文能够帮助初学者理解 POSIX 线程的基本概念和常用函数,为深入学习多线程编程奠定基础。

http://www.xdnf.cn/news/218215.html

相关文章:

  • Java SE(5)——数组
  • Java基础学习内容大纲
  • 【Qt】Qt换肤,使用QResource动态加载资源文件
  • AI时代来临将带来文科复兴
  • 预留库存的实现
  • 清晰易懂的跨域请求知识——拿捏
  • 前端与后端开发详解:从概念到就业技能指南
  • 解锁健康密码,开启养生新旅程
  • 空间权重矩阵
  • 体育直播源码NBA足球直播M29模板赛事直播源码
  • Linux日志系统大揭秘-系统故障排查安全审计
  • openssl_error_string() 不要依赖错误信息作为逻辑判断
  • JVM | CMS垃圾收集器详解
  • OVP UVP与UVLO对比
  • 2025年DDoS攻击防御全解析:应对超大流量的实战策略
  • Springboot使用jwt实现登录认证
  • Gitea windows服务注册,服务启动、停止、重启脚本
  • JavaScript面试问题
  • 研读论文——电子科技大学《通过专家混合实现多类型上下文感知的对话推荐系统》
  • antd-vue表单实现一个临时校验效果
  • DeepSeek+Dify之六通过API调用工作流
  • 头歌java课程实验(Java中的IO流操作)
  • python脚本下载ERA5数据详细规范和教程
  • Mysql中索引的知识
  • c#栈及其应用
  • 生物信息学常用软件InSequence,3大核心功能,简易好上手
  • PyTorch 深度学习实战(23):多任务强化学习(Multi-Task RL)之扩展
  • Redis Sentinel 和 Redis Cluster 各自的原理、优缺点及适用场景是什么?
  • pStubMsg--MemorySize0x74字节是如何分配的之rpcrt4!NdrAllocate函数分析
  • 项目三 - 任务1:采用面向对象方式求三角形面积