Linux pthread线程怎么创建与使用

70次阅读
没有评论

共计 4470 个字符,预计需要花费 12 分钟才能阅读完成。

本篇内容介绍了“Linux pthread 线程怎么创建与使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1. 前言

线程与进程的区别  

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

(2)线程: 是进程调度的最小单位,每个进程都有一个主线程。在进程里主要做事情就是线程。

(3)在全系统中,进程 ID 是唯一标识,对于进程的管理都是通过 PID 来实现的。每创建一个进程,内核去中就会创建一个结构体来存储该进程的全部信息,每一个存储进程信息的节点也都保存着自己的 PID。需要管理该进程时就通过这个 ID 来实现 (比如发送信号)。当子进程结束要回收时(子进程调用 exit() 退出或代码执行完),需要通过 wait()系统调用来进行,未回收的消亡进程会成为僵尸进程,其进程实体已经不复存在,但会虚占 PID 资源,因此回收是有必要的。

对于线程而言,若要主动终止需要调用 pthread_exit(),主线程需要调用 pthread_join()来回收(前提是该线程没有设置“分离属性”)。像线发送线程信号也是通过线程 ID 实现

进程间的通信方式: 

A. 共享内存 B. 消息队列 C. 信号量 D. 有名管道 E. 无名管道 F. 信号 G. 文件 H.socket 

线程间的通信方式: 

A. 互斥量 B. 自旋锁 C. 条件变量 D. 读写锁 E. 线程信号 F. 全局变量

进程间采用的通信方式要么需要切换内核上下文,要么要与外设访问(有名管道,文件)。所以速度会比较慢。而线程采用自己特有的通信方式的话,基本都在自己的进程空间内完成,不存在切换,所以通信速度会较快。也就是说,进程间与线程间分别采用的通信方式,除了种类的区别外,还有速度上的区别。

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

2. 线程相关函数介绍

2.1 创建线程

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

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

参数介绍

第一个参数为指向线程标识符的指针。第二个参数用来设置线程属性。默认可填 NULL。第三个参数是线程运行函数的起始地址。最后一个参数是运行函数的参数。不需要参数可填 NULL。Linux 下查看函数帮助:# man pthread_create

返回值:若线程创建成功,则返回 0。若线程创建失败,则返回出错编号。线程创建成功后,attr 参数用于指定各种不同的线程属性。新创建的线程从 start_rtn 函数的地址开始运行,该函数只有一个万能指针参数 arg,如果需要向线程工作函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为 arg 的参数传入。

示例:

#include  stdio.h 
#include  pthread.h 
// 线程函数 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 函数一样。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。

这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针,该返回值可以通过 pthread_join 函数的第二个参数得到。

函数原型

#include  pthread.h 
void pthread_exit(void *retval);

参数解析 线程的需要返回的地址。注意: 线程结束必须释放线程堆栈,就是说线程函数必须调用 pthread_exit()结束,否则直到主进程函数退出才释放

2.3 等待线程结束

pthread_join()函数,以阻塞的方式等待 thread 指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且 thread 指定的线程必须是 joinable(结合属性)属性。函数原型

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

参数 第一个参数: 线程标识符,即线程 ID,标识唯一线程。最后一个参数: 用户定义的指针,用来存储被等待线程返回的地址。返回值 0 代表成功。失败,返回的则是错误号。接收线程返回值示例:

// 退出线程
pthread_exit ( 线程已正常退出 
// 接收线程的返回值
void *pth_join_ret1;
pthread_join( thread1,  pth_join_ret1);

2.4 线程分离属性

创建一个线程默认的状态是 joinable(结合属性),如果一个线程结束运行但没有调用 pthread_join,则它的状态类似于进程中的 Zombie Process(僵死进程),即还有一部分资源没有被回收(退出状态码),所以创建线程者应该 pthread_join 来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于进程的 wait,waitpid)。但是调用 pthread_join(pthread_id)函数后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。

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

#include  pthread.h 
int pthread_detach(pthread_t thread);

参数 线程标识符 返回值 0 表示成功。错误返回错误码。EINVAL 线程并不是一个可接合线程。ESRCH 没有线程 ID 可以被发现。

2.5 获取当前线程的标识符

pthread_self 函数功能是获得线程自身的 ID。函数原型

#include  pthread.h 
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  stdio.h 
#include  pthread.h 
#include  stdlib.h 
// 线程清理函数
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  pthread.h 
函数原型:pthread_cancel(pthread_t tid);

“Linux pthread 线程怎么创建与使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-03发表,共计4470字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)