• 协程详解(一)


    1. 进程,线程,协程的概念
    什么是进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
    说人话就是,我们起一个程序就基本上是一个进程,也有可能是多个(有些程序需要多个进程配合),同一个程序我们也可以打开多个,例如打开多个微信,那么就等于起了多个进程。
     
    什么是线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
    简单来说,线程就是因为进程来过庞大而进行细分的一种结果。当一个程序需要多任务异步处理的时候,就需要多个进程来配合,但是多个进程的切换系统开销很大(寄存器、信号、分配内存等),于是就有了线程,可以使用线程来解决这种问题,线程属于进程的一部分,一个进程可以拥有多个线程,线程需要的资源非常小,cpu切换线程的开销非常小了,非常适用于异步操作。
    线程是系统调度的最小单位了。
     
    上面的一些概念大家估计都懂了,那什么是协程,按规矩先来一个正式的介绍
    协程:协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用
    一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
    有些文章说协程是用户级别的线程,其实这个说的也有偏差,毕竟线程是可以进行系统调度的,但是协程是不具备的,协程更像是多个子程序的协作者,协程通过自己的执行切换,让各个子程序能够用最高效的方式尽快执行完毕。协程可以自己指定执行的位置,例如有三个任务ABC,协程可以指哪打哪,指A就执行A,执行B就执行B,  指C就执行C.  
     
    其实还有另外一种解释,这个是从协程的诞生是为了解决什么问题的角度来讨论:
    在如今的web系统中,基本上都是使用异步io来处理的,基本上是不会使用同步的方式,

    IO无非就是几种

    1. 读(硬盘的)的
    2. 写(硬盘的)的
    3. 网络请求(读和写)

    所有的读和写,网络请求接口都要设置成非阻塞式的,当系统内核把这些玩意儿执行完毕以后,再通过回调函数,通知用户处理。在用户空间上来看,我们就一直保持在一个线程,一个进程之中,因此,这种速率极高!

    为什么Nodejs并发量会那么变态的原因就是这一点:nodejs因为js线程只有一条(底层的IO使用多线程来实现非阻塞),为了让程序不会卡住,就一定要把所有的IO变成非阻塞异步,通过回调来告诉用户。
    例如nodejs的代码:
    get_some('http://xxx.abc.com', function(){
    console.log('success')
    })

    那么问题就来了:

    1. 我们进行IO操作的目的是因为我们想获得某些数据然后再开始进行操作,所以我们不得不在回调函数中操作。
    2. 我们在回调函数中获取到我们想要的数据,我们依靠这段数据又他妈的想要获取另外一段数据,怎么办?继续回调。
    3. 一两层你可能就觉得不行了,45678层呢?

    协程的真正目的其实并不是为了解决高并发而存在的,而是为了解决这种蛋痛的无限回调而存在的

    使用协程之后,我们就可以像使用「同步」的方法去写异步的调用

    例如js 里面的promise, await, async这些就是协程的一种体现
    使用协程,上面的代码就可以写成
    let ret = await get_some('http://xxx.abc.com')
    console.log('success')
     
    这是js的解决方法,python的解决方法也是类似,python使用的库是asyncio
     
    所以应该知道了协程是一个什么样的存在了。没有什么高深的概念,从这个角度来看,协程就是把回调代码的编写方式变成了同步代码的编写方式
    从这里理解,协程并不是为了做并发,而是为了把同时做多件事,想把非常耗时每一件事的流程使用async await定义好流程,然后把多件事情组合在一起,一起协作去做,所以才叫协程,这个协字非常关键
     
    总结一下
    • 进程的切换 需要 切换系统资源和指令,消耗时间最长
    • 线程的切换,不需要切换系统资源,只需要切换指令、线程堆栈。但这个过程也需要系统调用。
    • 协程的切换都在用户空间内进行,不需要进行系统调用。

    协程优于线程的主要在于

    • python 线程调度方式是,每执行 100 个字节码或者遇到阻塞就停止当前线程,然后进行一个系统调用,让 os 内核选出下一个线程。但是协程 只会在 阻塞的时候,切换到下一个协程。100个字节码,说多不多,说少不少,你调用两个库函数说不定就没了,因此线程的切换存在很多是无效的切换,当线程数量越大,这种因为调度策略的先天不足带来的性能损耗就越大。
    • 线程需要进行系统调用,协程不需要。系统调用需要进入内核态,无效的调度会让这部分开销显得更大
    • 协程可以自主调度,而线程只能决定合适退出,但是下一个线程是谁则依赖于操作系统。
  • 相关阅读:
    Java堆、栈和常量池
    Java多线程内存模型
    To-do List
    Java Collections Framework 汇总
    关于ArrayList.clear()与=null以及new ArrayList<E>()
    开源协议
    git-svn — 让git和svn协同工作
    Java Collections Framework 之 RandomAccess接口
    转 : CSS Modules详解及React中实践
    转 : JBoss Web和 Tomcat的区别
  • 原文地址:https://www.cnblogs.com/wilken/p/14232981.html
Copyright © 2020-2023  润新知