目录
在前面对于进程包括其概念、创建、释放以及进程间通讯的方式等之后,我们再提到比进程还要轻量的一个执行序列就是线程。线程是轻量级的进程,换句话说线程是被进程所包含的,进程里中是存在线程的。
一、了解线程
我们从两方面去理解线程:一是线程是轻量级的进程;二是线程是进程内部的一条执行序列(一组有序指令),或者说是执行流,线程也是动态的概念。
轻量级:创建:创建进程时分配资源,后期创建线程时不会分配资源;
调度:线程的切换比进程调度速度快;
注意:
一个进程至少有一条线程,即就是main函数所代表的执行序列。称之为主线程。通过线程库就可以创建线程-----函数线程。
主线程仅仅代表进程执行的第一条线程而已。当主线程通过线程库创建出线程以后,所有的线程就无任何区别。
主线程默认结束,结束的是整个进程。
二、线程的实现方式
用户级:线程的创建、销毁、管理都是在用户空间完成,内核只会识别为一个进程,一条线程。优点是灵活性,操作系统不知道线程存在,在任何平台上都可以运行;线程切换块,线程切换在用户空间完成,不需要陷入内核;不用修改操作系统,实现容易。缺点是编程复杂,用户必须自己管理线程,包括线程调度;如果一个线程阻塞,整个进程都会阻塞,不能使用对称多处理器。
内核级:线程的创建、销毁、管理由操作系统内核完成。内核线程使得用户编程简单,但是每次切换都得陷入内核,所以效率较低。
混合模式:即就是一部分以用户级线程创建,一部分由内核创建,是一个多对多的关系。结合用户级和内核级的优点。
三、线程与进程的区别
(1)线程是(CPU)系统调用的最小单位,进程是资源分配的最小单位;
(2)线程依赖于进程,线程在进程内部,一个进程至少包含一条线程;
(3)进程都是相互独立的,同一个进程中的线程除了线程的栈区,其他资源都共享;
(4)进程通讯需要特殊的机制,县城通讯则可直接借助全局空间,或是堆区空间;
(5)线程存在线程安全问题;
(6)线程切换相比进程切换效率高
(7)创建线程比创建进程快,线程所消耗的资源比进程少。
四、线程库的使用
头文件#include<pthread.h>
1、线程创建
统一进程中的线程都是并发执行的,线程的执行顺序是不一定的,是由系统当前环境和线程调度算法决定的。
Int pthread_create(pthred_t *id, pthread_attr_t *arr,void*(*pthread_fun)(void*), void*arg);
Id:线程id 创建线程时分配的线程ID;传递一个变量的地址;
Attr:线程属性,默认为NULL
Pthread_fun:线程函数 新创建的线程的执行体,函数地址:指定新线程从那块开始执行;
Arg:传递给线程函数的参数,(arg的值会传给void*,传给所调用的函数);
返回值:assert(res==0);成功返回0,失败返回错误码;
创建进程和线程底层都是调用的是clone函数;
(1)值传递:最多传递四字节的数据,指针将传递的值直接强转为void*;
arg:类型是void* 记录传递的值
(2)地址传递:将要传递的值的地址转化为void*;
arg:类型是void* 记录的是传递的地址
地址传递时主线程后期对值的修改可能会影响函数线程中获取值
函数线程中通过地址对变量修改,也会影响主线程中变量的值;
Strace:限时进城执行过程中的所有的系统调用函数;
Ltrace:显示进程执行过程中的所有库函数;
2、线程结束
Int pthread_exit(void *);
注意:Main函数结束代表进程结束,exit(0),线程依赖于进程,进程结束,所有线程随之结束;
等待线程结束函数:int pthread_jojn(pthread_t id, void **retval);//等待线程结束,获取线程退出信息;
取消一个线程:int pthred_cancel(pthread_t id);//发出取消请求,并不会等待
五、多线程的数据共享问题
创建线程只会在进程资源中申请栈区资源用于线程函数的执行;只会分配虚拟地址的空间,当真正用的时候(即运行)才会映射到内存中;进程中的数据区域、堆区、多线程都是共享的,即使用同一份。
1、全局数据:共享的。虚拟地址相同。统一进程的多线程共享进程的4G虚拟地址空间。
2、堆区:共享的
3、栈区:不共享;创建线程时,将栈区数据的地址传递给函数线程,实现共享;通过该地址访问栈区;
4、文件描述符:共享的;文件资源试给进程分配的,同一进程所访问的表数组是一样的,指针是同一份。
由于对于多线程间有许多共享的资源,在线程间共享数据的方式:
将数据定义到全局;
将数据保存到堆区;
栈区---》创建线程时,将栈区数据的地址传递给函数线程;