进程/线程/协程的理解

进程、线程和协程

  • 进程:进程就是应用程序的启动实例,比如说打开一个软件,打开一个应用,就等于开启一个进程。进程拥有代码和打开的文件资源,数据资源,独立的内存空间。

  • 线程:从属于进程,是程序的执行者,一个进程至少包括一个主线程。当然也可以包含更多的子线程。线程有自己的栈空间。

  • 协程:一种比线程更加轻量级的存在,正如一个进程下有多个线程一样,一个线程下可以拥有多个协程。协程是人为开启的,不受操作系统的管理和调度。这也就是说协程不会像线程那样切换上下文环境,消耗资源。

  • 归纳:

    对于操作系统来讲,线程是最小的执行单位,进程是最小的资源管理单位,协程是人为控制,不受操作系统的管理和调度。

  • 多进程之间的通信

    1. 定义生产者/消费者模型
    2. 生产者写入数据到队列中,当队列容量已满的时候(限定队列长度),生产者属于阻塞状态。
    3. 消费者监听同步队列,当队列中有数据时需要拉取数据,否则就属于阻塞状态。
  • 多线程之间的通信

    • 为什么需要线程通信?

      在多线程并发操作时,cpu默认是是随机切换线程的,如果我们使用多线程来共同完成一件任务时,我们希望他们有规律的执行,而不至于多个线程对于同一共享数据的读写操作。所以多线程之间时需要一个协调机制的。

    • 怎么解决多线程通信问题

      ​ 对于多线程避免多个线程对于同一共享数据的操作,我们引出锁的概念。python已经给我们提供了Threading.Lock() 对象,添加锁[lock.acquire()],一次只能加一把锁,加多了就会出现死锁状态。 释放锁 [lock.release()] .

      ​ Threading.Rlock() 对象,在同一线程内可以多次加锁[lock.acquire()] ,不会出现死锁状态。释放锁[lock.release()] ,加锁和释放锁需要成队的出现。

  • 线程的生命周期

    • 新建状态,当程序实例化一个对象时,就完成了新建的状态。

    • 就绪状态,当创建的对象调用了start() 方法 则进入了就绪状态,start()方法是启动线程。

    • 运行状态,当就绪状态的线程被CPU调度,开始执行run()方法,就进入了运行状态。

    • 阻塞状态,当运行线程数大于处理器数的时候,运行状态的线程将会被阻塞,从而进入阻塞状态。

    • 死亡状态,当run执行完程序块时,或者在run执行过程中抛出异常或者错误时,线程则进入死亡状态。

      )

  • IO复用

    • select

      • 原理

        select是通过系统调用来监视着一个由多个文件描述符组成的数组,当select()返回后,数组中就绪的文件描述符会被内核修改标记位,使得进程可以获得这些文件描述符从而进行后续的读写操作。select是通过遍历来监视整个数组的,而且每次遍历都是线性的。

      • 优点

        select目前几乎在所有的平台上支持,良好的跨平台性。

      • 缺点

        • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多的时候会很大。
        • 单个进程能够监视的fd数量存在最大限制,在linux上默认为1024(可以通过修改宏定义或者重新编译内核的方式提升这个限制)
        • 并且由于select的fd是放在数组中,并且每次都要线性遍历整个数组,当fd很多的时候,开销也很大。
    • poll

      • 原理

        poll本质上和select没有区别,只是没有了最大连接数(linux上默认1024个)的限制,原因是它基于链表存储的。

      • 优点

        没有最大连接数的限制

      • 缺点

        • 每次调用poll,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多的时候会很大。
        • 并且由于poll的fd是放在数组中,并且每次都要线性遍历整个数组,当fd很多的时候,开销也很大。
    • epoll

      • 原理
      • 优点
        • epoll的解决方法是每次注册新的事件到epoll中,会把所有的fd拷贝进内核,而不是在等待的时候重新拷贝,保证了每个fd在整个过程中只会拷贝1次。
        • epoll没有这个限制,它所支持的fd上限是最大可以打开文件的数据,具体 数据可以cat /proc/sys/fs/file_max查看,一般来说这个数目和系统内存关系比较大。
        • epoll的解决方法不像select和poll每次对所有fd进行遍历轮询所有fd集合,而是在注册新的事件时,为每个fd指定一个回调函数,当设备就绪的时候,调用这个回调函数,这个回调函数就会把就绪的fd加入一个就绪表中。所以epoll只需要遍历就绪表
        • 水平触发:只要满足条件,就触发一个事件(只要有数据没有被获取,内核就不断通知你)。例如:在水平触发模式下,重复调用epoll.poll()会重复通知关注的event,直到与该even有关的所有数据都已被处理。
        • 边缘触发:每当状态变化时,触发一个事件。例如:在边缘触发模式中,epoll.poll()在读或者写event在socket上发生后,将只会返回一次event。调用epoll.poll()的程序必须处理所有的和这个event相关的数据,随后的epoll.poll()调用不会再有这个event的通知。

posted @ 2019-08-07 16:00  巫小诗  阅读(271)  评论(0编辑  收藏  举报