北京网站seo报价,中国互联网设计公司,做的比较好的电商网站,公司简介简短大气目录
一、认识线程
1.认识线程V1
2.认识线程V2
3.认识线程V3
4.认识线程V4
5.认识线程V5
二、线程控制
1.前言
2.创建线程
3.线程等待
4.线程终止
5.线程分离
三、线程理解 一、认识线程
1.认识线程V1 借用大多数计算机教材的话#xff0c;线程是进程的一个执行…目录
一、认识线程
1.认识线程V1
2.认识线程V2
3.认识线程V3
4.认识线程V4
5.认识线程V5
二、线程控制
1.前言
2.创建线程
3.线程等待
4.线程终止
5.线程分离
三、线程理解 一、认识线程
1.认识线程V1 借用大多数计算机教材的话线程是进程的一个执行分支线程是CPU调度的基本单位。 多进程的缺点在于每创建一个进程就要创建一个PCB对象一份地址空间一张页表想办法把这部分消耗缩小就只需新建一份PCB地址空间和页表共用一份代码部分再均分比如有5个进程就把所有调度函数均分为5份如此一来CPU并不知道自己是执行多进程只是在调度一个又一个PCB而用户看来CPU却是通过“多线程的方式”提高了效率。
2.认识线程V2 既然如此多个进程有多个线程系统中存在大量线程必然需要描述、组织然而Linux下并没有再单独定义线程结构体而是用进程的结构体来模拟线程。
3.认识线程V3
重新认识进程 以前认识进程这个进程是单执行流用现在的话说就是内部只有一个线程。 现在认识进程这个进程内部有多个线程多个执行分支。 总结的看进程是系统分配资源的单位而线程是系统调度的单位因此往后理解进程都要站在系统分配资源的角度去看。
4.认识线程V4 cpu调度角度 现在来看有的进程只有一个执行流cpu调度可以称为调度进程而有的进程有多个执行流cpu调度时称为调度线程。 Linux下为了统一这种含糊不清的概念把cpu调度的执行流统称为轻量级进程。线程 执行流 进程。因此cpu不再区分自己到底是在线程调度还是进程调度都称为执行流。 内核级虚拟机技术 我们现在看待进程它是操作系统分配资源的基本单位可以认为是一个容器那么如果一个进程所对应的代码部分是一个操作系统意味着这个操作系统支持内核级虚拟机技术。 5.认识线程V5
页表 抛出一个问题操作系统是怎么给多线程均分代码的 第一个结论内存本质上是有限个4KB的内存块定义为数组方便增删查改。 第二个结论页表并不是简单的K-V映射虚拟地址是有划分的比如32位的虚拟地址前10位是一个整体中间10位是一个整体后12位一般是页内偏移地址而页表其实是多张页目录和多张页内偏移表。 第三个结论多线程划分代码其实就是让每一个线程拿到自己代码所在的n张页内偏移表。 二、线程控制
1.前言 ps -aL指令查看LWPlight weight process。
ps -aL 过去要么是单进程单执行流要么是多进程多执行流而学习多线程后变成了单进程多执行流。 在内核层面cpu调度的是一个又一个的LWP只有轻量级进程。 Linux的内核代码没有线程结构体只有轻量级进程的结构体定义。而在用户层面要严格区分进程和线程。因此需要对内核的系统调用封装于是有一个库叫 pthread库这个库不属于内核代码但是安装Linux操作系统时必须安装这个库。 2.创建线程 pthread_create 通过man手册可以得知pthread_create这个函数用来创建一个新的线程使用这个函数需要包含头文件pthread.h由于在3号手册查到了这个函数因此说明这个函数不是系统调用是用户层封装的函数。此外man手册提示编译和链接时要加特定选项-pthread。 void* HandleTask(void* args)
{//新线程std::string threadname (char*)args;
} int main()
{//主线程pthread_t tid;pthread_create(tid,nullptr,HandleTask,nullptr);return 0;
}3.线程等待 在学习进程时父进程要等待回收子进程。而线程这快主线程也要等待新线程。
运行成功的多线程程序主线程一定是最后运行结束的。主线程退出进程退出如果主线程的主体代码运行完毕而新线程还在运行则主线程需要等待新线程的执行结果。 pthread_join 第二个参数是输出型参数。 void* HandleTask(void* args)
{//新线程std::string threadname (char*)args;int cnt 5;while(cnt){sleep(1);cnt--;}return (void*)111;
} int main()
{//主线程//创建pthread_t tid;pthread_create(tid,nullptr,HandleTask,(void*)name);//等待void* ret nullptr;int cnt 10;while(cnt){sleep(1);cnt--;}int abc pthread_join(tid,ret);std::cout abc- abc new thread ret- (long)ret std::endl;return 0;
}没有发生异常的情况下新线程的运行结果是可以通过参数手动获取到的。 如果任何一个线程出现异常(div 0 野指针)都会导致整个进程退出 同时说明多线程代码往往健壮性不好。 4.线程终止
线程returnpthread_exit 哪一个线程调用这个函数哪一个线程就被终止。
pthread_exit((void*)111);
pthread_cancel 这个函数由主线程调用用来发送终止信号。 //终止测试sleep(1);pthread_cancel(tid); 返回结果为-1表明不是正常退出。 5.线程分离 pthread_detach 新线程做线程分离
pthread_detach(pthread_self()); 主线程也可以主动分离掉新线程。
pthread_detach(tid); 线程分离后到底发生了什么什么样的情况需要线程分离 每一个新线程默认是需要让主线程等待的即主线程需要调用pthread_join函数。如果主线程不需要关心新线程的执行状态那么就可以将这个新线程分离。新线程分离后仍旧和其他线程共享资源并且保留自己原来的私有资源但是如果主线程调用pthread_join去等待这个新线程是会出错的即被分离的线程意味着不再需要主线程等待。不管怎么设计程序都建议让主线程最后一个退出。
三、线程理解 多线程相比多进程的优点 创建一个新线程的开销要比一个新进程小得多。
与进程的切换相比线程切换时操作系统所做的操作要少得多。
1.切换线程时需要切换上下文的寄存器相对少一点切换线程只需切换保存是哪一个线程的寄存器而切换进程还要多切换用来保持虚拟地址空间、页表的寄存器。
2.由于cpu缓存技术 加载内存中代码时是一次性加载多行而切换进程时前后的代码内容更大概率不是在相邻存储更大概率可能要刷新缓存而多线程共享代码大概率不需要刷新缓存。
线程私有的数据
1.线程的硬件上下文数据本质是cpu寄存器的值这部分数据调度线程
2.每一个线程都有自己的独立栈结构用来保证线程的常规运行 线程共享的数据
1.代码部分、全局数据
2.文件描述符表、页表、进程地址空间等等
C11的多线程
语言层的多线程其实是对pthread库的进一步封装。 更深的理解pthread_t 如何标识一个唯一的线程Linux下有这样两种设计在内核一层即操作系统层面并没有定义线程结构体而是定义了一个LWP结构体名为轻量级进程等同于线程内核里面LWP是唯一的。但是Linux内核对线程的各种操作控制并不同于我们理论上学习的线程操作控制因此Linux又做封装即封装好的pthread库编译链接多线程程序时必须要链接这个库本质是第三方库因为它既不属于语言也不属于操作系统调用。 pthread_t就是pthread库里面定义的概念。 加载动态库加载到物理内存后经页表映射到虚拟地址空间中的共享区在代码区中执行到pthread_t tid这行代码后cpu则跳转到共享区动态库已经加载对应的定义处执行这行代码。 pthread动态库对线程做了管理即定义了线程结构体也使用数据结构控制。 每一个线程都有对应的结构体这个结构体有的地方也叫tcb而结构体的起始地址就是pthread_t tid的值。 上文提到了线程私有的数据之一就是独立栈结构。也是在pthread库里面的线程结构体中定义的因为每一个线程都有对应执行的代码可能会创建局部变量这些变量就保存在这部分栈结构中。 线程局部存储全局变量归所有线程共有如果希望只写一份定义全局变量的代码但是实际上却是多个进程各有一份数据就可以用像下面这样定义这些值保存在线程局部存储空间中。 __thread int IngTime 0;