线程的概念和基本使用详解

什么是线程

在一个程序中的多个执行路线就叫做线程。
更准确的定义:
线程是一个进程内部的一个控制序列,或者说是执行流(轻量级的进程)。(CPU调度执行的单位
一个进程至少有一条线程,即就是main函数所代表的执行序列,称之为主线程。通过线程库可以创建线程——函数线程;主线程仅仅是代表进程执行的第一条线程而已,当主线程通过线程库创建出函数线程以后,两个线程就没有任何区别。
另外线程不拥有系统资源,线程会共享进程的系统资源。
在这里插入图片描述

线程管理

线程管理就是要维持线程的各种信息,这些信息包含了线程的各种关键资料,存放这些信息的数据结构称为线程控制表或线程控制块。
线程共享一个进程空间,很多资源都是共享的,并且这些资源信息都是在进程控制块(PCB),但也有一些资源不是共享的,这些不被共享的资源信息都在线程控制块。
评判标准:如果某资源不独享会导致线程运行错误,则该资源就由每个线程独享;而其他资源都由进程里面的所有线程共享。
因此创建线程,我们只会在进程资源中申请栈区资源,用于线程函数的执行。进程中的数据区域、堆区多线程都是使用同一份。
线程共享资源
地址空间 、代码区、数据区、堆、文件描述符、子进程、信号等信息
线程独享资源
程序计数器(PC)用于存放下一条指令所在单元的地址的地方、寄存器、栈、状态字

操作系统线程实现方式

对于线程管理有两种:一种是让进程自己来管理线程,第二种是让操作系统来管理线程。因此出现了内核态线程和用户态线程。由进程自己管理的就是用户态线程实现,由操作系统管理就是内核态线程实现。
1、用户级线程
内核并不支持多线程,多线程是用户态实现,用户代码就必须实现线程的创建、调度、销毁等工作。
优点灵活性,内核不用知道线程的存在,所以在任何操作系统上都能应用。线程切换速度快(每次切换不需要陷入内核)
缺点用户代码复杂,如果一条线程阻塞,操作系统只会看到进程阻塞,把CPU控制权交给另一个进程,这样则会造成整个进程阻塞,则整个线程阻塞。
2、内核级线程
内核级线程就是线程的创建和管理都是在内核实现的。这样操作系统同时保有进程控制块和线程控制块。
每个用户线程就是一个内核线程,线程的是实现在内核态,内核知道用户态有几条线程,用户态线程和内核态线程数目对应,一种n-n关系。操作系统可以对线程进行各种类似进程的管理,如线程调度,线程的资源分配,出现阻塞进行处理等措施。
优点:用户编程简单,因为线程的复杂性由操作系统承担,用户在编程时无需管理线程的调度,不需要担心执行,挂起等操作。
缺点效率低,每次线程切换都需要陷入内核,让操作系统调度;会占用内核稀缺的内存资源,一旦内核空间溢出,操作系统将停止运转。
3、组合级线程
用户态的执行系统负责进程内部线程在非阻塞是的切换;内核态的操作系统负责阻塞线程的切换,将这种方式称作组合级线程。
其中内核态线程数量较少,而用户态线程数量多,每个内核态线程可以服务一个或多个用户态线程,换句话说,用户态线程被多路复用到内核态线程上。
在这里插入图片描述

所以在我们分配线程时:

将需要执行阻塞操作的线程设为内核态线程。
不会阻塞操作的线程设为用户态线程。

Linux中线程实现方式

Linux实现线程的机制十分独特,从内核角度来说,它并没有线程的概念,Linux把所有的线程都当做进程来实现,内核并没有准备特别的调度算法或定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个进程都拥有唯一隶属于自己的task_struct,所以在内核中,它看起来就像是一个普通进程(只是线程和其他进程共享某些资源,如进程的地址空间 .data .bss .text .heap)。

Linux没有专门的线程模块,它只是通过实现进程,在创建进程时,传递一些参数,使得我们创建的进程和其他进程共享某些资源,如地址空间,变量等资源,这样我们就说这个进程不是进程了,而是一个线程。

线程库的使用

调用clone函数,传入某些共享的参数,将这个系统调用进行封装为创建线程库函数,再将其他系统调用进行分装,就是Linux系统上的线程库,线程库函数主要包括:创建线程函数pthread_create,它的系统调用就是clone;其他的库函数都调用了对应的系统调用。

创建线程

#include<pthread.h>
int pthread_create(pthread_t *id,pthread_attr_t *attr,void* (*pthread_fun)(void*),void *arg);

id:线程id,线程创建时分配的线程id,传递一个变量的地址
attr:线程属性,默认为NULL
pthread_fun:线程函数,新创建的线程的执行体,函数地址:指定新线程
arg:传递给线程函数的参数
返回值:assert(res == 0);成功返回0,失败返回错误码。
同一个进程中的线程都是并发执行,并且线程的执行顺序是不一定的,由系统决定。
创建时给函数线程传参的两种方式:
1、值传递——最多传递四字节的数据,(指针)
将传递的值直接强转为void*
arg:类型是void* 记录的是传递的值
2、地址传递
将要传递的值的地址转化为void*
arg:类型是void* 记录的是传递的地址

主线程后期对值得修改可能影响函数线程中获取值
函数线程中通过地址对变量得修改,也会影响主线程中变量的值。

main函数结束,进程结束;exit(0)线程依赖进程,进程结束,所有线程随之结束。

结束线程

void pthread_exit(void *reval);

等待线程结束,获取线程退出信息

int pthread_join(pthread_t id,void **reval);

取消一个线程:
只是发出取消请求,并不会阻塞

int pthread_cancel
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值