ConcurrencyModel

并行计算

两大类并行

多进程或多线程并行

race condition dead lock

流水线并行

non-blocking io event-driven system

函数式并行(Functional Parallelism)

单个任务可以被拆分成多个部分来处理,每个部分都是一个函数,数据经过这些函数的时候是并行处理的,其实就是流水线
mapreduce; 但是对于本来就运行多个不同类型的任务的系统,比如 web server database server 都是不适合用函数式并行的,因为cpu本来就在处理多任务

多线程线程池并发的缺点就在于,第一,池子本身有限制,第二线程虽小但是还是耗费资源的,当用户量达到一定程度的时候,可能资源紧张无法创建线程了,而且切换开销也变得很大,多线程就无法解决问题了。第三,对于IO密集的任务,开启线程来处理,可能大多数线程是在阻塞而不是在计算,CPU资源大大浪费,而且还占用内存等各种资源。在多线程并发服务模型当中,主线程相当于流水线第一级,所有的从线程相当于流水线第二级,流水2级+从线程并行得以提高效率。线程池主要是去掉了线程的创建销毁开销时间。这种模型适合用户请求的计算密集任务(前提是多线程能真的充分利用多核并行计算,任务之间最好还没有相关性)。在 Python 中由于多线程有GIL,因此计算密集型的任务反而不合适,对于IO需求型的任务,由于Python线程在底层调用C的IO时候会释放GIL,反而效率比做计算密集型任务更好(计算密集在Python中最好使用多进程)。

单线程(进程)异步并发模型,这个模型就是在一个大的主循环中,根据事件驱动,一般主循环都是一个框架,你写的应用一般在这个框架之上运行,比如tornado 的IOLoop,启动的过程其实是从框架本身先启动的,框架就像一个冰山水下的部分,你的应用通过一些继承调用之类的处于冰山上面,因此你调用的一些函数或者一些任务可以当做异步非阻塞的,即调用后就可以返回了,最终其实返回到IOLoop这个大主线循环当中,他会检查你的一些回调或者异步任务是否需要执行,然后执行,然后又回来不断的这样调度。框架本使用的各种系统调用IO,其实都是用非阻塞的比如epoll,kqueue,select这就是为啥在异步框架当中你最好不要写同步阻塞的任务,因为会把框架堵塞!tornado 的 HTTPServer其实就直接使用的是非阻塞的sock,你可以查看他继承的TCPServer中socket的绑定部分,sock.setblocking(0)设置的是非阻塞式的。异步并发流水线级别更多更精细,是一个循环的过程,整个循环代码就是一个流水。所有注册在主框架中的callback都会通过底层的非阻塞系统调用select,epoll等循环取出fd做读写准备的检查,如果事件发生就会进行读写任务了,不发生又会进入下一轮大循环了。所以对于高并发请求的处理,异步显得更加有力。循环还能充分利用CPU资源。但是,如果请求都是做高度计算密集任务,那么就不适合在异步框架内取处理,而应该在其他地方处理,比如如果是耗时请求且是异步返回则可以将请求任务放入消息中间件,由其他服务或进程代为处理。美团云则使用了Tornado主循环异步+多线程同步模型,通过队列和生产者消费者模型的方式,将队列中的请求取出来由TaskWorker线程以及其他的DBWorker线程负责处理,当然这些耗时任务请求都会立即返回给web端。