打开电脑后,就可以同时使用多个应用程序。可以一边上网,一边听音乐,一边微信聊天,一边用vscode写代码。
对于操作系统来说,它需要做的事情就是如何管理这些应用程序。用什么去管理呢?首先需要把这些应用程序抽象出来,叫做什么呢,就叫做进程吧。有些App运行起来,可能仅仅需要一个进程去维护应用的状态,而有些则需要多个进程。例如一个浏览器,某个进程负责渲染页面,某个进程负责播放音乐。像浏览器这种软件,可以打开多个窗口,每个窗口都有独立的进程去维护。
那么进程之间怎么交流呢?进程之间交流用的是IPC,这个暂且不表。
线程位于进程之中,可以理解为进程的孩子。他们都可以到自己家的私密小花园上玩耍。但不能到别的进程的私密小花园中玩耍。
如果把进程比作父母,线程可以比作孩子。有一项任务,去花园里除草。你只有一个孩子,那么完成除草这个任务如果需要30分钟的话。如果你有三个孩子,三个孩子一起去除草,大约只需要10分钟就能完成花园除草。
所以:多线程可以提高工作效率。 但是强招必自损,多线程自然也有多线程的缺点。
加入把线程分为两类,那么前面讲的线程,可以说是进程领养的孩子。这些孩子都需要向操作系统去申请领养。但是绿色线程就不同了,这是进程亲生的。本质上说:绿色线程实际上是一种模拟的线程
不是所有操作系统都能提供多线程的服务,如果上层想使用多线程,那么只能自己模拟。绿色线程因为是模拟线程,也有很明显的优点,例如易于创建和销毁。你跟你老婆想要个二胎,那是很容易的。但是如果你想去领养一个孩子,那么自然是需要向相关政府部门填写一些申请之类的文件。这个自然是效率要比自己生第一点。在编程语言中 Go, Haskell or Rust使用的就是绿色线程。家里有一个孩子,父母如果给孩子买了一件新衣服,那么自然是不需要争抢的。如果有多个小孩,那就必须要解决:新衣服给谁穿的难题? 这个问题自古以来就是难以解决,一般都是长子继承制,但是也有一些小儿子不服气,最后闹个家破人亡的局面。
资源:新衣服 使用者:大儿子,二儿子,小女儿 可能结果:
大儿子、二儿子,小女儿为了争抢衣服,大打出手,最终一不小心,衣服给撕烂了,谁也穿不上大儿子穿了二儿子穿了小女儿穿了这个在编程语言中可以表示为:
var a // 线程1 a = 1 // 线程2 a = 2 // 线程3 a = 3 // 线程4 并不去对变量a进行写操作,而是要去读取a的值,那么a的值是什么呢? a ?**一个CPU核心,同一时间内,只能执行一个任务。**无论上层的是什么语言,即使该语言运行着多线程的任务,任务之间也是轮流去使用一个CPU资源。
有时候,在单核CPU上,多线程也并不能提高程序性能。只有一个炒菜锅,一个厨师一天可以炒80盘酸辣土豆丝,一百个厨师一天估计只能炒40盘土豆丝。在整个炒菜的的任务上,无论再添加多少厨师,都无法提高一天炒的土豆丝的盘数。
异步在程序的边界产生,边界就是程序自己无法控制,只能寻求外部系统帮助的缝隙。例如:假如你自己本身是一种编程语言的话,你能跑步,你能吃饭,你能唱歌,这些都是你能力范围之内的事情。如果你想和拥有打电话的能力,你本身并不能打电话,你只能去买一个手机,你需要借助手机的能力。当边界产生时,也会产生时间的缝隙。因不可能想买手机就立马拥有手机,最快的快递可能也需要一段时间。
君子生非异也,善假于物也。《荀子·劝学》
程序在赋值,条件判断,或者循环的时候。这些都是同步的,但是当程序需要去外部获取资源时,这时候就产生了异步的边界。
举例来说:妈妈在炒菜的时候突然发现没有酱油了,让小新去买酱油。小新到小卖部是需要花费时间的。妈妈这时候有两种策略,
在厨房里等着,啥也不做,直到小新把酱油买回来,再炒菜,这叫同步。先洗洗菜,做米饭,等小新什么时候回来,什么时候再炒菜,这叫异步。 $.get('酱油') .done(function(res){ // 继续做菜 mother.continueCook() }) .fail(function(err){ // 为什么没买到酱油 console.log(err) }) mother.doSomethingElse()原子性是指一个操作是无法分割的。你可能以为一个赋值语句a = 1是原子的,是无法分割的。但是在应用层是原子操作,当这个操作逐渐下沉的时候,你永远不知道这个赋值语句会被拆分成多少条语句。实际上,任何语言都是无法保证原子性的。
const a = 1转自https://github.com/wangduanduan/wangduanduan.github.io/issues/204