进程、线程、异步是什么
进程是程序的一次执行过程,运行中需要使用cpu,磁盘,网络等操作系统资源。并且是独占式的。线程是进程内部的一条执行分支,进程内的多个线程资源共享,可以访问同样的cpu、磁盘、网络等资源。进程和线程都可以独立申请cpu资源。有些编程语言提供了异步的编程方式,在代码里可以通过异步的方式来做到类似子线程的功能,其实最底层实现还是通过线程池来做的(见《深入浅出nodejs》)。
主线程代码的拆分与线程安全问题
程序中代码是可以串行的顺序的执行完的,但有的操作什么时候执行完不确定,比如读取文件、访问网络、访问硬件等,这些耗时操作的时间是不可控的,如果采用单进程、单线程的方式,会导致程序一直阻塞在那里,没法做别的事情。涉及到用户交互、涉及到ui的程序,肯定不能这样做,需要把这段耗时操作放到别的进程、线程、异步代码中去做,在主线程或者主进程里处理用户交互。但是有的操作也不能随意的分出去,比如涉及到同一资源操作的代码,分成多个线程会导致并发、线程安全问题,需要用锁、事务等来解决,比如数据库解决并发修改同一份数据问题的事务。
安卓中把与渲染相关的所有操作都放在主线程去做,子线程只能做一些网络请求、文件读取等一些耗时的与界面无关的操作,就是为了避免并发修改的问题。H5的web worker也是一样,只能主线程操作dom。通过限制访问的方式,避免了并发的各种复杂处理。
android、ios应用因为在操作系统之上,可以直接用操作系统提供的进程、线程的功能。而js运行在js vm之上,前端代码更是运行在浏览器的沙箱里,处理H5提供的Web Worker外,在应用层面涉及不到线程的处理。
服务端的多线程模型、异步模型和多进程架构
服务端都是多线程的,每个请求一个线程来处理,比如servlet容器,他会每个请求创建一个线程,然后传给具体的servelt,这也简化了javaweb的开发,不需要在应用层面去处理线程的分配和释放,应用层面的代码都是同步的。当然涉及到高并发的场景,靠tomcat的线程管理是不够的,我们需要去了解concurrent并发包里的api去手动处理。
都说服务端的线程模型受cpu的线程数上限的限制,而异步不会,其实不是这样的,异步也是用线程池实现的,只不过多线程模型是耗时操作一直阻塞的占用着线程,而异步是动态的申请和释放线程,通过把上下文独立于线程来避免了线程资源的浪费,异步操作结束,把结果返回主线程。
node的 server并不像java的tomcat容器那样做了线程的管理,更何况js的异步根本不需要我们在应用层面去操作线程,语言层面已经封装了。应用层面的单线程会有异常处理,资源冲突等问题,可以结合多进程来扩展,像eggjs就是封装了多进程的模型,结合进程和异步可以开发企业级的应用。
进程、线程、异步的关系如图所示:
总结
因为一些应用服务器的封装,多线程模型可以在开发过程中感受不到多线程的处理,而异步模型需要在应用层处理异步。但这没本质区别。只是一个封装在了语言层面,一个封装在了服务器层面。异步的方式确实比多线程的方式对线程资源利用率高。两种模型都可以通过多进程来扩展。
作者:凌霄光
链接:https://www.jianshu.com/p/78f2a10a899a