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


就这样进程诞生了 。
CPU执行的第一个函数也起个名字 , 第一个要被执行的函数听起来比较重要 , 干脆就叫main函数吧 。
完成上述两个步骤的程序也要起个名字 , 根据“弄不懂原则”这个“简单”的程序就叫操作系统(OperatingSystem)好啦 。
就这样操作系统诞生了 , 程序员要想运行程序再也不用自己手动加载一遍了 。
现在进程和操作系统都有了 , 一切看上去都很完美 。
从单核到多核 , 如何充分利用多核
人类的一大特点就是生命不息折腾不止 , 从单核折腾到了多核 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
这时 , 假设我们想写一个程序并且要分利用多核该怎么办呢?
有的同学可能会说不是有进程吗 , 多开几个进程不就可以了?听上去似乎很有道理 , 但是主要存在这样几个问题:
进程是需要占用内存空间的(从上一节能看到这一点) , 如果多个进程基于同一个可执行程序 , 那么这些进程其内存区域中的内容几乎完全相同 , 这显然会造成内存的浪费计算机处理的任务可能是比较复杂的 , 这就涉及到了进程间通信 , 由于各个进程处于不同的内存地址空间 , 进程间通信天然需要借助操作系统 , 这就在增大编程难度的同时也增加了系统开销该怎么办呢?
从进程到线程
让我再来仔细的想一想这个问题 , 所谓进程无非就是内存中的一段区域 , 这段区域中保存了CPU执行的机器指令以及函数运行时的堆栈信息 , 要想让进程运行 , 就把main函数的第一条机器指令地址写入PC寄存器 , 这样进程就运行起来了 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
进程的缺点在于只有一个入口函数 , 也就是main函数 , 因此进程中的机器指令只能被一个CPU执行 , 那么有没有办法让多个CPU来执行同一个进程中的机器指令呢?
聪明的你应该能想到 , 既然我们可以把main函数的第一条指令地址写入PC寄存器 , 那么其它函数和main函数又有什么区别呢?
答案是没什么区别 , main函数的特殊之处无非就在于是CPU执行的第一个函数 , 除此之外再无特别之处 , 我们可以把PC寄存器指向main函数 , 就可以把PC寄存器指向任何一个函数 。
当我们把PC寄存器指向非main函数时 , 线程就诞生了 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
至此我们解放了思想 , 一个进程内可以有多个入口函数 , 也就是说属于同一个进程中的机器指令可以被多个CPU同时执行 。
注意 , 这是一个和进程不同的概念 , 创建进程时我们需要在内存中找到一块合适的区域以装入进程 , 然后把CPU的PC寄存器指向main函数 , 也就是说进程中只有一个执行流 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
但是现在不一样了 , 多个CPU可以在同一个屋檐下(进程占用的内存区域)同时执行属于该进程的多个入口函数 , 也就是说现在一个进程内可以有多个执行流了 。
书圈|看完这篇还不懂高并发中的线程与线程池你来打我(内含20张图)
文章图片
总是叫执行流好像有点太容易理解了 , 再次祭出”弄不懂原则“ , 起个不容易懂的名字 , 就叫线程吧 。
这就是线程的由来 。
操作系统为每个进程维护了一堆信息 , 用来记录进程所处的内存空间等 , 这堆信息记为数据集A 。
同样的 , 操作系统也需要为线程维护一堆信息 , 用来记录线程的入口函数或者栈信息等 , 这堆数据记为数据集B 。
显然数据集B要比数据A的量要少 , 同时不像进程 , 创建一个线程时无需去内存中找一段内存空间 , 因为线程是运行在所处进程的地址空间的 , 这块地址空间在程序启动时已经创建完毕 , 同时线程是程序在运行期间创建的(进程启动后) , 因此当线程开始运行的时候这块地址空间就已经存在了 , 线程可以直接使用 。 这就是为什么各种教材上提的创建线程要比创建进程快的原因(当然还有其它原因) 。