0

0

Linux系统编程-(pthread)线程创建与使用

爱谁谁

爱谁谁

发布时间:2025-07-13 08:44:02

|

1084人浏览过

|

来源于php中文网

原创

1. 引言

在前面的文章中,我们已经讨论了Linux下进程的创建、管理、使用和通信,了解了多进程并发的工作方式。现在,我们将转向Linux下线程的基本使用。

线程与进程的区别

(1)进程:是操作系统调度的最小单位。可以通过ps、top等命令查看进程的详细信息。

(2)线程:是进程内部调度的最小单位,每个进程都有一个主线程。线程是进程内执行任务的基本单位。

(3)在整个系统中,进程ID是唯一的,通过PID来管理进程。每创建一个新进程,内核都会生成一个结构体来保存该进程的所有信息,每个节点也都包含自己的PID。需要管理进程时,使用这个ID(例如发送信号)。当子进程结束需要回收时(子进程调用exit()退出或执行完毕),需要通过wait()系统调用来回收,否则未回收的子进程会变成僵尸进程,虽然进程实体已经不存在,但仍占用PID资源,因此回收是必要的。

对于线程,如果需要主动终止,应调用pthread_exit(),主线程则通过pthread_join()来回收(前提是该线程未设置为“分离属性”)。同样,发送线程信号也是通过线程ID实现的。

进程间的通信方式包括:共享内存、消息队列、信号量、有名管道、无名管道、信号、文件、socket。线程间的通信方式有:互斥量、自旋锁、条件变量、读写锁、线程信号、共享全局变量。

进程间的通信通常需要切换内核上下文或访问外部设备(如有名管道和文件),因此速度较慢。相比之下,线程间的通信主要在自己的进程空间内进行,无需上下文切换,所以速度更快。也就是说,进程和线程的通信方式不仅在类型上有区别,在速度上也有差异。

说明:当运行多线程的进程捕获到信号时,仅主线程会被阻塞,其他子线程不会受到影响,继续执行。

  1. 线程相关函数介绍

2.1 创建线程

pthread_create是Unix操作系统(如Unix、Linux等)中用于创建线程的函数。编译时需要指定链接库:-lpthread。函数原型如下:

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

参数说明:

第一个参数是指向线程标识符的指针。第二个参数用于设置线程属性,默认可以填NULL。第三个参数是线程运行函数的起始地址。最后一个参数是传递给运行函数的参数,无需参数时可填NULL。Linux下可以通过命令# man pthread_create查看函数帮助。

Linux系统编程-(pthread)线程创建与使用

返回值:如果线程创建成功,返回0;如果失败,返回错误编号。线程创建成功后,attr参数用于指定各种线程属性。新创建的线程从start_rtn函数的地址开始执行,该函数只有一个通用指针参数arg。如果需要传递多个参数给线程工作函数,可以将这些参数封装在一个结构体中,并将结构体的地址作为arg传递。

示例:

#include 
#include 

// 线程函数1 void pthread_func1(void arg) { while(1) { printf("线程函数1正在运行.....\n"); sleep(2); } }

// 线程函数2 void pthread_func2(void arg) { while(1) { printf("线程函数2正在运行.....\n"); sleep(2); } }

int main(int argc, char **argv) { pthread_t thread_id1; pthread_t thread_id2;

// 1. 创建线程1
if(pthread_create(&thread_id1, NULL, pthread_func1, NULL)) {
    printf("线程1创建失败!\n");
    return -1;
}

// 2. 创建线程2
if(pthread_create(&thread_id2, NULL, pthread_func2, NULL)) {
    printf("线程2创建失败!\n");
    return -1;
}

// 3. 等待线程结束,释放线程的资源
pthread_join(thread_id1, NULL);
pthread_join(thread_id2, NULL);

return 0;

} // gcc pthread_demo_code.c -lpthread

2.2 退出线程

线程可以通过调用pthread_exit函数终止执行,类似于进程在结束时调用exit函数。这个函数的作用是终止调用它的线程并返回一个指向某个对象的指针。

函数原型:

#include 
void pthread_exit(void *retval);

参数说明:

参数为线程需要返回的地址。注意:线程结束时必须释放线程堆栈,因此线程函数必须调用pthread_exit()来结束,否则直到主进程函数退出才会释放。

2.3 等待线程结束

pthread_join()函数以阻塞的方式等待指定的线程结束。当函数返回时,被等待线程的资源被回收。如果线程已经结束,该函数会立即返回。被等待的线程必须是可连接的(joinable属性)。

函数原型:

#include 
int pthread_join(pthread_t thread, void **retval);

参数说明:

第一个参数是线程标识符,即线程ID,唯一标识一个线程。第二个参数是用户定义的指针,用于存储被等待线程返回的地址。

返回值:

0表示成功,失败时返回错误号。

千博购物系统.Net
千博购物系统.Net

千博购物系统.Net能够适合不同类型商品,为您提供了一个完整的在线开店解决方案。千博购物系统.Net除了拥有一般网上商店系统所具有的所有功能,还拥有着其它网店系统没有的许多超强功能。千博购物系统.Net适合中小企业和个人快速构建个性化的网上商店。强劲、安全、稳定、易用、免费是它的主要特性。系统由C#及Access/MS SQL开发,是B/S(浏览器/服务器)结构Asp.Net程序。多种独创的技术使

下载

接收线程返回值示例:

// 退出线程
pthread_exit("线程已正常退出");

// 接收线程的返回值 void pth_join_ret1; pthread_join(thread1, &pth_join_ret1);

2.4 线程分离属性

创建一个线程时,默认状态是可连接的(joinable属性)。如果一个线程结束运行但没有调用pthread_join,它的状态类似于进程中的僵尸进程(Zombie Process),即还有一部分资源未被回收(退出状态码)。因此,创建线程者应该使用pthread_join等待线程运行结束,并获取线程的退出代码,回收其资源(类似于进程的wait和waitpid)。但在某些情况下,我们不希望调用者被阻塞。

pthread_detach函数可以将线程的状态设置为分离状态(detached),这样线程运行结束后会自动释放所有资源。

函数原型:

#include 
int pthread_detach(pthread_t thread);

参数说明:

参数为线程标识符。

返回值:

0表示成功,错误时返回错误码。EINVAL表示线程不是可连接线程,ESRCH表示没有找到指定的线程ID。

2.5 获取当前线程的标识符

pthread_self函数用于获取线程自身的ID。

函数原型:

#include 
pthread_t pthread_self(void);

返回值:

返回当前线程的标识符。pthread_t的类型为unsigned long int,因此在打印时应使用%lu格式,否则显示结果可能出错。

2.6 自动清理线程资源

线程可以安排在退出时调用的函数,这些函数称为线程清理处理程序,用于在程序异常退出时进行资源清理。在POSIX线程API中,提供了pthread_cleanup_push()/pthread_cleanup_pop()函数用于自动释放资源。从pthread_cleanup_push()调用点到pthread_cleanup_pop()之间的程序段中,任何终止操作(包括调用pthread_exit()和异常终止)都会执行pthread_cleanup_push()指定的清理函数。

注意:pthread_cleanup_push函数与pthread_cleanup_pop函数必须成对调用。

函数原型:

void pthread_cleanup_push(void (routine)(void ), void arg); // 注册清理函数
void pthread_cleanup_pop(int execute); // 释放清理函数

参数说明:

void (routine)(void ):处理程序的函数入口。void *arg:传递给处理函数的参数。int execute:执行状态值,0表示不调用清理函数,1表示调用清理函数。

导致清理函数调用的条件:

调用pthread_exit()函数时,pthread_cleanup_pop的参数为1。注意:return不会导致清理函数调用。

2.7 自动清理线程示例代码

#include 

include

include

// 线程清理函数 void routine_func(void *arg) { printf("线程资源清理成功\n"); }

// 线程工作函数 void start_routine(void dev) { pthread_cleanup_push(routine_func, NULL); // 终止线程 // pthread_exit(NULL); pthread_cleanup_pop(1); // 1会导致清理函数被调用,0不会调用。 }

int main(int argc, char *argv[]) { pthread_t thread_id; // 存放线程的标识符

// 1. 创建线程
if(pthread_create(&thread_id, NULL, start_routine, NULL) != 0) {
    printf("线程创建失败!\n");
}

// 2.设置线程的分离属性
if(pthread_detach(thread_id) != 0) {
    printf("分离属性设置失败!\n");
}

while(1) {}

return 0;

}

2.8 线程取消函数

pthread_cancel函数用于取消同一进程中的其他线程。

函数原型:

#include 
int pthread_cancel(pthread_t tid);

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

232

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

436

2024.03.01

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

182

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

280

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

254

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

121

2025.08.07

全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

78

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

96

2025.09.18

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

23

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.4万人学习

Git 教程
Git 教程

共21课时 | 2.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号