1.线程
-
线程是CPU调度的最小单位
-
eg:单核CPU,有3个要执行的线程,先执行线程1,让出时间片,再执行线程2,让出时间片,再执行线程3,直至所有线程执行完毕;
左右两边的区别在于:右边不对CPU进行时间分片,右边只执行了两次线程的上下文切换 ,两侧执行的总时间是一样的
-
疑问:右边的执行效率更高?多线程存在的意义?
意义:I/O:包括DiskIO(耗时)和NetworkIO
在读取文件的过程中,CPU并不直接读取硬盘,而是对DMA下达指令,让DMA完成文件的读取。
步骤:
(1)CPU向DMA下达指令,指令中含有磁盘设备信息和需要读取的文件位置;
(2)DMA告知硬盘进行文件的读取,文件读取会将硬盘中的内容加载到内存中;
(3)硬盘读取完毕后,硬盘会给DMA一个反馈;
(4)DMA最终以中断的形式通知CPU:文件读取完成
(5)最后,CPU从内存中去读取变量的值,即拿到了文件的内容
-
CPU在(1)状态后,就处于闲置的状态,CPU可以去执行其他的线程
假设这3个线程都是需要读取文件,看最右边的紫色线,CPU先让1号线程执行,1号线程执行:让DMA进行读取,此时CPU让出资源交给其他的线程,接着线程2拿到CPU的控制权,他通知DMA去进行文件的读取,接着线程3也是一样操作。。。。
此时CPU的等待时间X可以让给其他线程。
文件读取完成后,三个DMA以中断的方式形式通知CPU,他们的时间点分别为y1,y2,y3,接着线程1又拿到CPU资源,又可以接着进行操作
-
意义的总结:X这块的执行权(就是DMA的执行的过程中),这块的CPU不是阻塞的,CPU可以交给其他的线程,其次,CPU总线是可以复用的,DMA可以充分的利用这些总线,通过这两点可以提高CPU的利用率
-
缺点:线程是OS底层的api,线程开辟浪费时间,运行线程也会造成线程上下文的切换,用户态和内核态的转换,浪费CPU切换的时间
2.协程
- 编程语言级别的线程,可以像使用线程一样使用协程,但是在OS底层,他并不是线程
- 协程全程处于用户态,可以大量开辟,不用考虑用户态和内核态的转化,
- 在一台机器上,开辟上千个线程是极限,go语言开辟协程的数量可以达到千万级别
3.异步
- 在1中的(4)DMA最终以中断的形式通知CPU:文件读取完成就是异步操作
- Node.js是单线程的,却可以应对高并发,就是因为他是异步的。在Node.js中有大量的回调函数的产生,大量的异步操作
- eg:在单线程的执行过程中,出现了IO读写,就会交给DMA去进行文件的读写,单线程继续工作,最终在触发的时候,会触发一个回调函数,获取到一个文件的数据