【Linux】—— Linux线程控制

Linux下线程的控制

创建线程

  • 由于Linux下不可以像创建进程一样直接用命令创建线程,因此这里我们得先介绍一下POSLX线程库

POSLX线程库

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以 pthread_ 打头的
  • 要使用这些函数库,要通过引入头文件<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的-lpthread选项

创建线程函数原型

创建线程函数

  • thread:该参数是一个指针,当线程创建成功时,用来返回创建的线程ID
  • attr:该参数用于指定线程的属性,NULL表示使用默认属性。
  • start_routine:该参数为一个函数指针,指向线程创建后要调用的函数。这个被线程调用的函数也称为线程函数
  • arg:该参数指向传递给线程函数的参数。

线程创建成功时,pthread_create函数返回0,若不为0则说明创建线程失败。线程创建成功后,新创建的线程开始运行第三个参数所指向的函数,原来的线程继续运行。

了解创建线程的函数之后,我们接着来使用一下这个函数
创建线程
我们来看一下执行结果
创建线程
从执行结果中我们可以看到的确有两个线程在并发执行各自的代码,但是我们又怎么清楚的看到自己创建的新线程呢,这里我们来介绍一条命令ps -ajL 该命令可以帮助我们查看线程相关信息
线程
我们可以看到前两个线程的PID是相同的但是不同的是LWP,那什么是LWP呢,其实LWP就是我们线程的ID,我们在之前也提过进程ID,进程ID是标识唯一的一个的进程的,同样LWP线程ID也是用来标识唯一的一个线程的

用户态 系统调用
线程ID pid_t gettid(void);
进程ID pid_t getpid(void);

了解了这些概念之后我们来修改一下我们的代码,获取一下我们的pthread_create的一个参数,这里说的线程ID和我们刚刚通过ps -ajL命令获取的线程ID并不是同一个,前一个是因为线程操作系统调度器的最小单位,该线程ID用于唯一标识该线程的,而我们现在获取的pthread_create的一个参数指向的一个虚拟内存,该内存单元的地址即为新创建线程的ID
线程ID
我们可以看到这串数字很大,我们将其转换为16进制看一下,我们看着个序列好像有点像一个地址,没错其实就是一个地址,而该地址就是我们线程的ID
线程ID

线程ID即进程地址空间布局

  • 这里再强调一次,我们通过pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。这个线程ID和前面说的LWP表示的线程ID不是一回事。
  • 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
  • pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  • 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:
    获取线程ID
    我们刚刚将获取的的pthread_ create函数的第一个参数转换为一个16进制序列之后也发现它很像一个地址,其实对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址进程地址空间布局

线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。
  • pthread_exit函数
    pthread_exit

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

  • pthread_cancel函数
    pthread_cancel

线程等待

线程等待的原因其实和进程等待的原因相似,避免线程退出之后,其空间未被释放,从而导致内存泄漏的问题,很好理解,那我们介绍一下让线程等待的函数

  • pthread_join函数
    pthread_join

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数
    线程等待
    了解了概念我们就来用一下这个函数
    线程等待
    执行结果
    线程等待

线程分离

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
    分离线程

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:

pthread_detach(pthread_self());

这里要注意一点就是:joinable和分离是冲突的,一个线程不能既是joinable又是分离的

还是接着用一下这个函数
分离线程

在这里插入图片描述
我们看运行结果,和错误返回码,我们就知道刚刚强调的线程分离和线程等待时冲突的,一个线程是不可能又是分离又是等待的。

总结

这里我们讲了有关线程控制的相关函数,创建线程,终止线程,线程等待,线程分离等,这些函数都不难理解,主要还是得多练习才能够熟练的使用。

发布了167 篇原创文章 · 获赞 175 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chenxiyuehh/article/details/96602767
今日推荐