Liunx学习-----多线程(2)-----线程的创建与使用


首先,在Linux中,系统并没有之间给出线程的操作接口,我们所使用的,都是一些人为的封装的实现的线程库。从而实现在用户态创建一个进程,但是在linux下的进程是通过pcb完成的,因此在用户态创建一个进程同时,也必须在内核创建出一个pcb出来,这时候,我们吧用户态创建的线程叫做用户态线程,将内核创建的pcb叫做轻量级进程。

如下图:
在这里插入图片描述

线程的创建

1.创建线程的函数:
头文件为#include<pthread.h>
①:函数接口:int pthread_create(pthread_t *tid, pthread_attr_t *attr, void*(*thread_routine)(void* arg), void* arg)
②:各参数的含义:

  • tid:用于获取线程id。
  • attr:用于设置线程的属性。一般我们传为NULL,表示使用默认的属性。
  • (thread_routine)(void arg):线程的入口函数,其中arg为线程入口函数的参数。
  • arg:就是线程入口函数的参数。如果线程入口函数的参数无用,可以传入NULL。

其中,创建成功返回0,不成功返回非0值。

2.创建进程,如下:
在这里插入图片描述
其中:
我们创建线程时,得先满足创建线程函数的第一个参数,所以我们得先:

pthread_t tid;

这个其实就是一个地址,,也就是我们创建线程所在共享区去分配的一个地址,如下图:
在这里插入图片描述
所以tid其实就是我们创建这个线程的地址,也是用于区别一个进程中多个线程的。
还有就是如果说res返回值不为0,那么其非0返回值其实就是一个错误码,
对于上述的代码,我们运行的情况如下:
在这里插入图片描述
由于是无限循环,所以会一直打印,并且我们可看出来,打印的顺序是不同的,所以创建出来的线程,就会自己去跑,会和其他线程去争夺在cpu上运行,所以运行的顺序就会有所不同。

其中,我们还有查询线程是否运行的命令:ps -efL:其中,L就是查看轻量级进程,如下:
在这里插入图片描述

而没个位置对应的解释为:
在这里插入图片描述
其中LWP就表示的是轻量级进程的id号。

线程的终止

线程的终止就是退出一个线程。
但是如果我们吧当前线程运行的函数运行完毕了,这个线程就会退出。
终止线程的方法,这个终止就是对于线程还没运行完当前函数的终止(人为的):
在Linux下,我们终止线程一共有三种方法:

  1. 在线程入口函数中return。(但是注意的是,我们不能轻易在main函数中就去return,因为main函数退出,其他所有旗下进程全部退出)
  2. void pthread_exit(void *retval);:可在任意位置调用,调用后退出调用线程。
    注意:这个函数没有返回值,但是参数retval其实就是这个进程退出的返回值。(retval在线程当前线程退出后,保存这个线程的返回值,如不需要,可设置为NULL)
  3. int pthread_cancel(pthread_t tid);:在任意位置调用指定pid的线程。(其中参数就是我们所指定的线程)

注意:pthread_exit和return返回所指向的内存单元必须是全局的或者malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

线程的等待

我们指定,进程的退出是需要等待的,因为进程退出需要释放资源,在父子进程中,如果子进程先于父进程的退出,并且父进程没有接收到子进程的信号,此时子进程就变为僵尸进程,资源泄漏,造成严重的后果。
其实对于线程是一样的,在线程退出的时候,如果不去等待线程,那么线程也会存在有资源泄漏,也会变成僵尸线程。(因为进程和线程的状态类型都是相同的)。
所以,我们对于一个线程的退出,也必须去等待,防止造成不可收拾的后果。

等待:等待一个指定的线程退出,获取这个线程退出的返回值,释放资源,默认情况下,如果一个线程退出,如果不等待也会造成资源泄漏。

为什么要等待呢?
①:已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
②:创建新的线程不会复用刚才退出线程的地址空间。

1.等待的函数接口:int pthread_join(pthread_t tid , void** retval);
其中:
①:tid:表示要等待的哪个进程退出。(tid就是该进程)
②:retval:是一个void*的空间地址,用于接收线程返回值。

其中:对于retval,在不同的退出情况下,得到的返回值是不同的:

  • 如果线程是通过return返回,则retval保存的值是其线程函数的返回值的指针。
  • 如果线程是通过pthread_cancel返回,则retval保存的值是常数,PTHREAD_CANCELED的指针。
  • 如果线程是通过pthread_exit返回,则retval保存的是传递给thread_exit的参数的指针。
  • 如果我们对其返回值不感兴趣,也可也传递NULL进去。

其中:这个retval需要注意一下。
因为这个参数主要是为了接收线程的返回值,并且还是个二级指针,所以我们传递的时候,最好传递是的一级指针的地址,不要传递二级指针,如果说我们传递的二级指针是一个NULL指针,那么在等待函数内部进行操作的时候,就会出现错误,因为系统会对其进行解引用,对NULL指针进行解引用,可想而知,是多么大的错误。

注意:对于默认情况下的线程退出,为了保存自己的返回值,因此线程占用的资源在退出后也不会完全释放,需要被其他线程等待。

其实:线程等待与进程等待不同,线程等待有时候不仅仅是因为为了释放资源,避免资源泄漏而等待,还有的因为是我们必须等待一个进程运行完成后,才能开始继续往下去处理,还有就是等待某个或者所有线程退出后才能继续运行而去等待的。

线程的分离

线程的分离是从线程的等待为前提发展出来的。
是因为有时候,我们对于一个线程,我们完全不需要关心这个线程怎么样,把他分出去了,他就自己干自己的,和我们的主线程完全没有任何影响,我们不关心它的返回值,也不关心它是否有没有运行完,所以,如果这个时候我们去等待这个线程,是不是有点多余了,并且有时候为了等待它们退出,而去堵塞起来,会导致性能损失。所以我们就出现了线程分离。

线程分离:
做法:将线程的分离属性设置为detach状态。
我们一般对于进程是没有进行分离属性的设置,所以是按默认的分离属性操作进行,因为默认的是-JOINABLE,表示进程退出后不会自动释放资源,需要被等待。而我们将这个分离属性自己给出,给出为**-DETACH**,表示的是线程推出后不需要被等待,而是直接释放资源。

注意:

  • 如果将线程的属性设置为-DETACH属性,那么线程退出后不需要被等待,而是直接释放资源。
  • 线程一点设置了分离属性,推出后就自动释放资源,那么等待将毫无意义,所以一旦设置了分离属性的线程不能被等待。

设置分离属性的接口:

int pthread_detach(pthread_t tid);

还有一个接口:

pthread_self();//获取自己的tid。
pthread_detach(pthread_self());//分离自己。

在实际使用时:对于分离和等待,对一个进程只能有一种操作,所以我们应该根据需求去使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值