Linux:线程之线程控制

        一个程序在运行时,会被加载到内存成了进程。如果一个进程需要做很多的事情,那么为了提高进程的工作效率,会找一些"小弟"来替代进程做一些事情,这个"小弟"就是线程。

        进程在创建的时候需要创建资源、创建PCB,所以进程也就是承担分配资源的实体,而线程就是进程给线程分配的资源,让线程去执行。

        由此我们可以区别一下进程与线程:进程拥有资源,并且拥有分配资源的能力,是操作系统资源分配的基本单位。而线程仅仅是用着进程所分配的资源给进程做事情,线程是CPU调度的基本单位。

线程

 

线程的概念

  •  Linux下没有真正的线程,线程是进程中的一条执行流。线程是内核执行的最小单位,linux下的线程都是用进程模拟的,所以linux下的线程又叫做轻量级进程。
  • 一个进程至少有一个执行线程。而同一个进程的多个线程共享同一地址空间,因此同一个进程的所有线程共享数据段和代码,但每个线程都有自己的栈空间,因此线程间通信就非常简单。
  •  线程间共享的资源:文件描述符、信号的处理方式、用 户id和组id、堆空间和代码段等。 线程私有的资源:栈空间、上下文、线程id、信号屏 蔽字,调度优先级等。

 

 线程的共享与独有

    共享:虚拟地址空间、文件状态表、信号处理方式 、用户id和组id、当前工作目录

    独有(相对独有,因为独有的数据其实还是在共享的虚拟地址空间里): 栈区、上下文数据、线程id、errno、信号屏蔽字。

 

线程和进程

  • 线程的相比于进程的优缺点:

       优点:线程的创建和销毁成本更低;

                   线程的调度切换成本更低;

                   线程间的通信更加方便;

                   线程的执行粒度更加细致;

       缺点:缺乏访问控制---->线程安全;

                   有些系统调用和程序异常是针对整个进程产生影响;

                    多个进程对临界(公共)资源进行操作有可能造成数据混乱;

 

  • 线程与进程的区别与联系

        <1>进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。

        <2>进程间相互独立,而同一进程中的线程则共用进程的虚拟地址空间(共享堆空间、数据段和代码段)。

        <3>进程因为独立性所以健壮性比较强,但通信麻烦,资源成本高;线程间通信非常方便,资源成本低,但缺乏访问控制,需要更多考虑线程的安全性。

 

多线程/多进程

    io密集型程序----->分摊等待

    cpu密集型程序--->分摊计算


线程控制

        操作系统并没有提供相对应的系统调用来实现线程控制的接口,所以有人就为线程的控制封装了一套线程库(POSIX线程库,使用这套接口创建的线程称之为用户态的线程,但是它在操作系统内部对应了一个轻量级进程(pcb)。

 

线程ID

        在Linux下引入了线程组,多线程的进程被称作线程组。 线程组内每一个线程都拥有自己的进程描述符,在自己的进程描述符内部的PID,其实是这个线程的线程ID,而其内部有一个TGID,含义为Thread Group ID这才是真正我们所看到的PID。也就是说其实我们看到的PID实际上是线程组ID。而所有线程组内部的线程,它们的PID是不同的,但是它们的TGID肯定相同。我们把它们共同的这个TGID称作进程PID。 

       在线程组中,线程组的ID取决于主线程的线程ID,而我们所说的进程PID其实就是主线程的线程ID。 我们可以用gettid()来获取线程的线程ID

       获取线程自身ID:pthread_self(void),它获得的其实是我们在创建线程时所传入的tid,相当于一个标识符,这个标识符能够让线程库更好的对线程进行操作等。而并不是我们想要的线程ID。

        使用pthread_create函数时会产生一个线程ID,存放在第一个参数指向的地址中,这个线程ID并不是我们理解的线程ID,这个线程ID是线程库为了方便操作这些线程而使用的ID。而我们说的线程ID是进程调度范畴的,线程是轻量级进程,是调度器调度的基本单位,所以需要一个唯一的ID与之对应,这样可以更好的去调度。
 
 

POSIX线程库

线程的所有调用函数都不是系统调用,并且在链接是需要手动链接线程库-lphread:

  • 线程创建:pthread_create (&tid, NULL, thr_start, NULL)
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg);
//功能:创建一个线程
//参数:
//thread:返回线程ID
//attr:设置线程属性,attr为NULL表示使用默认属性
//start_routine:函数地址,表示要执行的函数
//arg:给线程要执行的函数传参数
//返回值:
//成功返回0,失败返回错误码
  •  

  • 通过参数返回一个用户态的线程id----->这个线程id是线程地址空间在进程虚拟地址空间中的首地址;
  •  这个用户态线程的操作都是围绕这个用户态的线程id来操作的;
  •  每一个线程都是一个task_struct,也就意味着每个线程都有一个pid,但是ps命令只能显示一个,task_struct中不仅有pid还有一个tgid(线程组id     等于 线程组领导者的pid),那么ps看到的pid实际上是这个tgid(线程组id), 因此Linux下的进程就成了线程组;
  •  pthread_self这个接口用于获取线程自身id---->tid,tid同时也是该线程的首地址,通过tid可以找到该线程;
  •  查看线程信息可以使用ps -L命令, LWP这一列显示的就是线程pcb中的pid。

 

  •   线程终止:pthread_exit  / pthread_cancel
#include <pthread.h>
void pthread_exit(void* value_ptr);
//功能:线程终止
//参数:
//value_ptr:value_ptr不要指向一个局部变量
//返回值:无返回值


int pthread_cancel(pthread_t thread)
//功能:取消一个执行的线程
//参数:
//thread:线程ID
//返回值:成功返回0,失败返回错误码
  •  不能在main中return,不能调用exit,因为这两个都是退出进程的,进程一退出所有的线程也就退出了。
  •  pthread_exit :谁调用谁退出  (退出自己)
  •  pthread_cancel:取消其他线程,让其他线程退出  (退出别人)

    

  •  线程等待:pthrea_join  获取其他普通线程的退出返回值,并且允许操作系统回收线程资源,避免产生僵尸线程
#include <pthread.h>
int pthread_join(pthread_t thread, void** value_ptr);
//功能:等待线程结束
//参数:
//thread:线程ID
//value_ptr:输出型参数,指向一个指针,这个指针指向线程的返回值
//返回值:成功返回0,失败返回错误码
  • 线程只有在处于joinable状态(线程默认属性)时才能被等待。
  • 如果线程是被取消而退出的,那么线程的返回值为-1.(pthread_cancel)

 

  •  线程分离:pthread_detach  分离一个线程,当线程退出后直接释放资源,无法获取返回值,不能被等待。
int pthread_detach(pthread_t pthread)//分离线程
  • 设置线程的属性为分离属性(detach),退出后直接释放资源,处于分离状态的线程是不能被等待的。
  • 只有在不关心线程的退出返回值时才会设置detach,因为一旦设置就无法获取返回值(无法pthread_join)。

 

    

 

 

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值