书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)

_本文原题为:看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
一切要从CPU说起
你可能会有疑问 , 讲多线程为什么要从CPU说起呢?原因很简单 , 在这里没有那些时髦的概念 , 你可以更加清晰的看清问题的本质 。
CPU并不知道线程、进程之类的概念 。
CPU只知道两件事:
1.从内存中取出指令
2.执行指令 , 然后回到1
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
你看 , 在这里CPU确实是不知道什么进程、线程之类的概念 。
接下来的问题就是CPU从哪里取出指令呢?答案是来自一个被称为ProgramCounter(简称PC)的寄存器 , 也就是我们熟知的程序计数器 , 在这里大家不要把寄存器想的太神秘 , 你可以简单的把寄存器理解为内存 , 只不过存取速度更快而已 。
PC寄存器中存放的是什么呢?这里存放的是指令在内存中的地址 , 什么指令呢?是CPU将要执行的下一条指令 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
那么是谁来设置PC寄存器中的指令地址呢?
原来PC寄存器中的地址默认是自动加1的 , 这当然是有道理的 , 因为大部分情况下CPU都是一条接一条顺序执行 , 当遇到if、else时 , 这种顺序执行就被打破了 , CPU在执行这类指令时会根据计算结果来动态改变PC寄存器中的值 , 这样CPU就可以正确的跳转到需要执行的指令了 。
聪明的你一定会问 , 那么PC中的初始值是怎么被设置的呢?
在回答这个问题之前我们需要知道CPU执行的指令来自哪里?是来自内存 , 废话 , 内存中的指令是从磁盘中保存的可执行程序加载过来的 , 磁盘中可执行程序是编译器生成的 , 编译器又是从哪里生成的机器指令呢?答案就是我们定义的函数 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
注意是函数 , 函数被编译后才会形成CPU执行的指令 , 那么很自然的 , 我们该如何让CPU执行一个函数呢?显然我们只需要找到函数被编译后形成的第一条指令就可以了 , 第一条指令就是函数入口 。
现在你应该知道了吧 , 我们想要CPU执行一个函数 , 那么只需要把该函数对应的第一条机器指令的地址写入PC寄存器就可以了 , 这样我们写的函数就开始被CPU执行起来啦 。
你可能会有疑问 , 这和线程有什么关系呢?
从CPU到操作系统
上一小节中我们明白了CPU的工作原理 , 我们想让CPU执行某个函数 , 那么只需要把函数对应的第一条机器执行装入PC寄存器就可以了 , 这样即使没有操作系统我们也可以让CPU执行程序 , 虽然可行但这是一个非常繁琐的过程 , 我们需要:
在内存中找到一块大小合适的区域装入程序找到函数入口 , 设置好PC寄存器让CPU开始执行程序这两个步骤绝不是那么容易的事情 , 如果每次在执行程序时程序员自己手动实现上述两个过程会疯掉的 , 因此聪明的程序员就会想干脆直接写个程序来自动完成上面两个步骤吧 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
机器指令需要加载到内存中执行 , 因此需要记录下内存的起始地址和长度;同时要找到函数的入口地址并写到PC寄存器中 , 想一想这是不是需要一个数据结构来记录下这些信息:
struct***{void*start_addr;intlen;void*start_point;...};接下来就是起名字时刻 。
这个数据结构总要有个名字吧 , 这个结构体用来记录什么信息呢?记录的是程序在被加载到内存中的运行状态 , 程序从磁盘加载到内存跑起来叫什么好呢?干脆就叫进程(Process)好了 , 我们的指导原则就是一定要听上去比较神秘 , 总之大家都不容易弄懂就对了 , 我将其称为“弄不懂原则” 。