python多线程与协程一些面试题总结

[协程]

什么是协程

协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是协程,协程是一种用户态的轻量级线程。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈。因此,协程能保留上一次调用的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法,进入上一次离开时所处逻辑流的位置。
最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是有程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那么怎么利用多核CPU呢?最简单的方法是多进程加协程,既充分利用多核,有充分发挥协程的高效率,可获得极高的性能。

输入a,b,c,d四个整数,打印(a+b)*(c+d)的值。假设a+b的过程是耗时1秒IO操作
"""
使用协程的概念,达到以下目的, 输入a,b,c,d四个整数,打印(a+b)*(c+d)的值
"""
import asyncio, os
from threading import current_thread

定义负责计算两个数字的和的协程

async def sum(a, b):
    print("【%s-%s】coroutine start to do: %s + %s" % (os.getpid(), current_thread().getName(), a, b))
    await asyncio.sleep(1) # 模拟耗时1秒的IO操作,自动切换协程
    r = int(a) + int(b)
    print("【%s-%s】coroutine end for : %s + %s,  result is %s" % (os.getpid(), current_thread().getName(), a, b, r))
    return r

定义主函数

def main(a, b, c, d):
    loop = asyncio.get_event_loop()
    task = asyncio.gather(
        sum(a, b),
        sum(c, d)
    )
    loop.run_until_complete(task)
    r1, r2 = task.result()
    r = r1 * r2
    print("【%s-%s】%s * %s = %s" % (os.getpid(), current_thread().getName(), r1, r2, r))
    loop.close()

if name == 'main':
    main(1, 2, 3, 4[IO多路复用]

[IO多路复用]

什么是io多路复用

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
  (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
  (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
  (3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
  (4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
  (5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
  与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。


select、poll、epoll之间的区别(搜狗面试)
(1)select==>时间复杂度O(n)
它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。
(2)poll==>时间复杂度O(n)
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.
(3)epoll==>时间复杂度O(1)
epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))

[多线程]

什么是GIL

GIL即全局解释器锁,全局解释器锁的存在,在同一时间内,python解释器只能运行一个线程的代码,这大大影响了python多线程的性能。而这个解释器锁由于历史原因,现在几乎无法消除。
python GIL 之所以会影响多线程等性能,是因为在多线程的情况下,只有当线程获得了一个全局锁的时候,那么该线程的代码才能运行,而全局锁只有一个,所以使用python多线程,在同一时刻也只有一个线程在运行,因此在即使在多核的情况下也只能发挥出单核的性能。
简述多线程多进程在调度上的差异
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保持状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的应用场景

协程的适用场景: 当程序中存在大量不需要CPU的操作时(IO),适用于协程。

线程与进程之间的关联

线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

进程间通信的方式

进程间通信(IPC,Interprocess communication),方法包括管道(PIPE)、消息队列

、共用内存以及套接字(Socket)。

Top