Linux线程概念和线程控制

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/DX_Jone/article/details/101022777

本文重点知识:

1.线程概念:
2.线程之间的资源独有与共享:
3.多线程处理任务的优缺点:
4.线程控制:

一.线程的概念:

1.什么是线程??

在操作系统中使用pcb来描述一个程序的运行,因此pcb就是一个进程;但是在Linux下没有为线程设计一个pcb来控制线程的运行,因此线程此时就是以进程pcb模拟实现的;Linux下以pcb模拟实现线程,因此Linux下的线程实际上是一个轻量级进程;这个轻量级进程因为共用大部分进程资源;相较于传统进程更加轻量化;

2.进程和线程之间的关系:
  • 进程是资源分配的基本单位----因为程序运行时资源分配是给整个线程组(进程)的;
  • 线程是CPU调度的基本单位—因为Linux pcb是线程;
  • 同一个进程组中的pcb共用同一个虚拟地址空间,共享进程组中的大部分资源;
3.线程的工作原理:线程之间资源的独有与共享;

线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。线程可以创建和撤消线程,从而实现程序的并发执行。一般,线程具有就绪、阻塞和运行三种基本状态;

线程独有资源:

  • 栈区:
  • 寄存器: 寄存器是线程独有的数据,也成为线程的上下文,主要是为了反映 了上次运行时寄存器的状态,这部分应该是线程独有的;
  • errno:错误码 独有数据,假设我们在一个线程中出现一个错误的时候我们的在其他线程中也会出现此errno,所以我们需要errno独有;
  • 信号屏蔽字: 在每个线程中可能在代码中的逻辑会出现不一样的信号屏蔽集;
  • 优先调度集: 计算机操作系统给任务指定的优先等级它,决定任务在使用资源时的优先次序;
  • 线程IO: 文件描述符,因为在一个线程中打开的文件在其他线程中应该是不能被使用的,所以我们的线程IO应该是独有的;

线程共享资源:

  • 共享虚拟地址空间:
  • 文件描述符表:
  • 当前工作路径:
  • 用户ID和组ID:
  • 信号处理方式:
  • 数据段和代码段:
4.线程处理任务的优缺点:
  • 多线程处理任务的优点:
    (1)线程间的通信除了进程间的方式之外还有更简单的就是全局数据—因此线程间的通信方式更加方便;
    (2)创建或销毁一个线程的成本比小会一个进程的成本更低;
    (3)线程间的调度相对于进程要低;

  • 多线程处理任务的缺点:
    (1)线程之间缺乏访问控制,有些系统调用或异常针对的是整个进程;
    (2)稳定性相较于进程更低;(一个线程退出,整个进程都得退);

二.线程控制:

Linux下操作系统并没有提供线程的控制系统调用接口;因此大佬们封装了一套线程控制接口库;使用库函数实现创建的线程我们称之为用户态线程,这个用户态线程在内核中使用一个轻量级进程实现调度;

Linux下的线程:用户态线程+轻量级进程; 用户通过用户态线程来控制程序的调度,是实际上程序的调度要通过用户态线程的描述信息进而去控制这个轻量级进程去完成;

线程控制的接口都是库函数实现的:创建一个用户态线程让用户控制,但是程序额调度处理都是通过轻量级进程pcb来实现的;

1.线程创建:

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

thread:用于获取线程ID----线程地址空间在整个虚拟地址空间的首地址;
attr:设置线程属性,通常置NULL;
start_routine:线程的入口函数;
arg:传递给线程函数的参数;

返回值:0表示成功;非0表示创建失败;

关于线程id的概念: 每一个线程都是一个pcb,task_struct都有一个pid,但是用户使用ps命令的时候查看线程缺只有一个线程,也就是只有一个进程的进程pid
但是在task_struct中含有LWP,PID,TID,其中LWP是task_strcut指向的pid,就是我们的轻量级进程id,PID是task_struct中的tgid,也就是线程组中的中线程pid,就是我们常说的进程id。我们的线程也是含有线程信息的,在共享内存中有一块内存存储的描述信息,就是栈和数据等等,TID就是我们描述信息的内存首地址,也就是我们的线程id(pthread_t);

2.线程终止:
线程终止就是线程在此程序中退出,其中退出的方式包含了主动和被动退出,
(1)主动退出:

  • return退出:
    但是在线程入库函数中和主线程中是不一样的,在main中return是退出整个进程,在线程入库函数中是退出我们的该线程;
  • 退出线程自身,谁调用谁退出;
void pthread_exit(void* retval)         
            
retval:线程的退出返回值;

注意:线程退出也会形成我们的僵尸线程,主线程退出进程并不会退出,因为线程退出也要保存自己的退出返回值;

(2)被动退出:

  • 取消其他线程;让其他线程退出;
int pthread_cancel(pthread_t thread);  
		  
thread:要取消的线程的ID;

注意:线程退出后默认不会自动释放资源,(保存自己的退出结果咋线程独有的地址空间中);因此也会造成资源泄漏;主线程退出,其他线程依然可以正常运行;

3.线程等待:
等待线程退出,获取退出线程的返回结果,释放退出线程资源;

一个线程创建出来,默认有一个属性叫joinable;处于joinable属性的线程退出后,不会自动释放资源,需要其他线程等待才能释放;处于jonable属性的线程必须被等待,否则造成资源泄漏;

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

pthread_t thread:回收的线程
void** retval:线程退出返回值

返回值:成功返回0,失败返回errno;
功能:阻塞等待指定线程退出;通过retval获取返回值;

4.线程分离:
将线程的joinable属性修改为detach属性;若线程处于detach属性,则线程退出后将自动回收资源;并且这个线程不需要被等待,等待是毫无意义的,因为线程退出返回值占用的空间已经被回收了;


#include <pthread.h>

pthread_detach(pthread_t tid);

pthread_t thread:被设置的线程id;

被设置后线程处于EINVAL状态,这个状态就是线程不能被等待,或者已经被其他线程分离

线程分离的适用场景:对线程的返回值不关心;线程分离可以在任意线程中实现;

猜你喜欢

转载自blog.csdn.net/DX_Jone/article/details/101022777
今日推荐