进程与线程

577 阅读5分钟

本文原创:zhaojiaojiao

进程

进程是动态的,一般由程序、数据集合和进程控制块三部分组成,程序用来描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;进程控制块(PCB)包含进程的描述信息和控制信息,是进程存在的唯一标识

PCB

  1. 作用是描述进程,对进程进行有效的控制和管理;具体体现为,进程创建时,系统会创建对应的PCB,当进程状态发生变化时,系统会将状态记录在PCB中,当进程执行结束时,系统回收其PCB
  2. 记录的信息主要有:进程标识符、进程当前状态、进程队列指针、程序和数据地址、进程优先级、CPU现场保护区、通信关系、家族关系、资源清单(不同操作系统的进程控制块结构不同)

进程状态

  • 就绪状态:具备执行条件
  • 执行状态:正在运行的状态
  • 阻塞状态:暂时无法执行下去

状态转换.png

线程

线程是程序执行中的一个单一的顺序控制流程,是CPU调度的最小单位,一个进程有一个或者多个线程,各个线程之间共享进程的内存空间,一个标准的线程由线程ID、当前指令指针(PC),寄存器和堆栈组成

线程的栈被自动分配到进程的内存空间中

线程和进程的区别

  • 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位

  • 一个进程由一个或者多个线程组成,线程是一个进程中代码的不同执行路线

  • 进程之间相互独立,但是同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(打开文件和信号),某进程内的线程在其它进程中不可见

  • 调度和切换:线程上下文切换比进程上下文切换要快的多

Node.js中的进程和线程

进程

Process模块

process 对象是一个全局变量,它提供有关当前 Node.js 进程的信息并对其进行控制。 作为一个全局变量,它始终可供 Node.js 应用程序使用,无需使用 require()。 它也可以使用 require() 显式地访问(摘自Node Process):

const process = require('process');

通过process我们可以设置或获取进程的信息,如下所示:

const http = require('http');

const server = http.createServer();
server.listen(3000,()=>{
    process.title='开启进程';
    console.log('进程id',process.pid)
})

控制台输出

进程id 77193

process输出.png

进程的创建

Node中提供了child_process模块用来创建子进程,下面列出了创建异步进程的方法,其他的方法可查看 child_process介绍

  • child_process.exec:可以衍生一个 shell 然后在该 shell 中执行 command,并缓冲任何产生的输出
  • child_process.execFile:与exec类似,区别是默认情况下是不能衍生shell的
  • child_process.fork:是 child_process.spawn() 的一个特例,专门用于衍生新的 Node.js 进程,衍生的 Node.js 子进程独立于父进程,但两者之间建立的 IPC 通信通道除外。 每个进程都有自己的内存,带有自己的 V8 实例。 由于需要额外的资源分配,因此不建议衍生大量的 Node.js 子进程。
  • child_process.spawn:使用给定的 command 衍生一个新进程,并带上 args 中的命令行参数

child_process.exec()child_process.execFile() 方法还允许指定可选的 callback 函数,当子进程终止时会被调用。

下面是创建子进程的示例:

process.js

const http = require('http');
const url = require('url');
const fork = require('child_process').fork;

let server = http.createServer(function(req, res) {
    let pathname = url.parse(req.url).pathname;
    if (pathname == '/time') {
        let child = fork('./childProcess.js');
        child.send('start');
        child.on('message', (msg) => {
            console.log('get result: ', new Date(+msg));
            res.end(msg);
        });
    }
});
server.listen(3000, '127.0.0.1');

childProcess.js

const http = require('http');

let getTime = () => {
    return Date.now() + '';
};

process.on('message', (msg) => {
    console.log('子进程接收到的:', msg);
    let time = getTime();
    process.send(time);
})

在浏览器中访问:http://127.0.0.1:3000/time,会有如下的展示:

浏览器预览结果.png

控制台的输出如下:

进程控制台输出.png

线程

在Node的10.5.0版本中,worker_threads 为Node提供了多线程的能力,与 child_processcluster 不同, worker_threads 可以共享内存。

模块中常用到的:

  1. isMainThread:是否是主线程

  2. parentPort:可以用来与主线程交换信息,可通过parentPort.postMessage()发送的信息会在主线程中通过worker.on('message')接收

  3. workerData:通过主线程中传递过来的数据, 它可以是任意的JavaScript值,通过主线程构造函数中的选项对象的workerData传递(拷贝传递)

  4. MessagePort 类:线程通信,继承自 EventEmitter

  5. MessageChannel类: 用于创建异步、双向通信的通道实例。

  6. Worker类:在主线程中创建子线程,第一个参数为 filename,表示子线程执行的入口

const {
    isMainThread,
    parentPort,
    workerData,
    Worker
} = require('worker_threads');


if (isMainThread) {
    const worker = new Worker(__filename, { workerData: 1 });
    worker.on('message', msg => {
        console.log(`main: receive ${msg}`);
        worker.postMessage('parent')
    });
} else {
    console.log(`worker: workerDate ${workerData}`);
    parentPort.on('message', msg => {
        console.log(`worker: receive ${msg}`);
    });
    parentPort.postMessage(workerData);
}

相关输出如下:

线程控制台输出.png


欢迎计算机前端相关领域小伙伴加入我们,具体的招聘信息可进入公众号查看,欢迎关注。

关注我们吧.jpg

本文由博客一文多发平台 OpenWrite 发布!