进程
进程:Process/Task,是程序的动态执行。操作系统将可执行文件从磁盘调入内存,为其分配必需的可执行代码内存和数据空间,这一过程就创建了一个进程——进程是操作系统分配内存的基础单位。
线程
线程:Thread,是进程内部执行代码的基础单位。一个进程内可以同时创建多个线程,并发的执行多端代码。
每个线程需要自己独立的数据内存空间,大小为2MB;所以,一台具有8GB内存的计算机,理论上可以同时存在4000个线程。
进程和线程的关系
进程是操作系统创建任务分配内存的基本单位;
线程是进程内部执行代码的基本单位;
线程处于进程内部。
一个进程内必须至少存在一个线程;
一个进程内也可以同时存在多个线程,这些线程间并发执行(线程切换计算速度快,宏观上是在并发执行,微观上的真正的并发执行取决于计算机的处理器核数)。
单线程服务器
早期的"单线程服务器"中,服务器端只有一个线程,依次为客户端请求提供服务。某一个时刻只能为一个客户端提供响应。
特点:
程序设计简单;
运行效率太低。
多线程服务器
为了同时处理多个客户端请求,很多服务器应用都涉及为多线程运行模式,即每个接收到一个个客户端连接请求,都会有创建一个专门的服务线程。
多线程模型的特点
多线程模型作为广泛采用的服务器运行模型,被大量的服务器程序所采用(如Apatch Httpd)。对于CPU密集型请求,可以充分利用多核CPU优势,同时为尽可能多的客户端提供服务。
但是,多线程模型也存在下列问题:
(1)每个线程内部的操作都是线性执行的,耗时操作会阻塞后续操作;
(2)受限于线程总数的限制,无法并发的处理大量的请求;
(3)过多过频繁的线程上下文切换,产生了更多的CPU开销;
(4)多线程并发往往伴随着互斥和死锁等问题,增加了程序设计复杂度。
Node.js中的单线程服务器
为了解决多线程服务器在高并发的I/O密集型应用中的不足,同时避免早期简单单线程服务器的性能障碍,Node.js采用了基于"事件循环"的非阻塞式单线程模型,实现了如下两个目标:
(1)保证每个请求都可以快速响应;
(2)实现远超过多线程模型的并发连接数。
提示:Node.js在JS层面是单线程的——没有创建新线程的机制。但是在底层的C/C++层面是多线程的,即访问底层操作系统服务时,存在多个并发工作线程的情形——使用了线程池。
阻塞执行
阻塞(Block):也称为同步执行(Synchroize),只有前面的操作全部执行完成,才能开始后续操作。(常规的多线程服务器内部都是采用的是阻塞执行)
var conn=mysql.createConnection(...); //步骤1
var result=conn,query('INSERT...'); //步骤2
conn.end(); //步骤3
非阻塞执行
非阻塞(Non—block):也称为异步执行(Asynchronize),后面的操作不必等待之前操作的执行完毕,可以先执行。
const fs=require('fs');
console.log('读取请求数据'); //操作1
fs.writeFile('app.log','访问日志',()=>{ //操作2
console.log('写出操作日志');
})
console.log('输出响应数据'); //操作3
//读取请求数据
//输出响应数据
//写出操作日志
异步回调
Node.js中的业务代码,都是在单一的主线程中执行的;当遇到耗时的阻塞操作时(如文件IO、网络访问、数据库请求等),不会等待其执行完毕,而是注册一个处理函数执行结果的回调函数,继续执行后续的代码。
待耗时的阻塞操作执行完成时,其对应的回调函数会转入回调函数队列,主线程在下次事件循环时会执行这些回调函数。
同步函数调用:
const fs=require('fs');
var data=fs.readFileSync('app.log');
console.log('文件内容:',data);
console.log('程序执行完成!');
//文件内容:121212
//程序执行完成!
异步函数调用:
const fs=require('fs');
fs.readFile('app.log',function(err,data){
console.log('文件内容:',data)
})
console.log('程序执行完成!');
//程序执行完成!
//文件内容:121212
事件驱动
事件驱动编程
同步编程模型:
var conn=openConnection();
var data=conn.readDate();
response.writeData(data);
事件驱动的异步编程模型:
requset.on('getData',()=>{
var conn=openCionnction();
conn.on('open',()=>{
var data=readDate();
data.on('complete',()=>{
response.sendData();
return;
})
})
})
事件循环
注意:虽然对异步操作很擅长,它可以用多次事件回调的形式来处理。但是一次事件回调里,需要大量CPU操作,他会阻塞后续所有的其他操作,其他的事件回调都不会执行。
setImmediate(()=>{
console.log('immdiate func...')
});
while(true){
}
console.log('脚本执行完成...')
//一直处于等待,其他事件回调不会执行,也不会有其他输出