最细最有条理解析:事件循环(消息循环)是什么?进程与线程的定义、关系与差异

目录

事件循环:引入

一、浏览器的进程模型

1.1、什么是进程(Process)

1.2、什么是线程(Thread)

1.3、进程与线程之间的关系联系与区别

二、浏览器有哪些进程和线程

2.1、浏览器的主要进程

①浏览器进程

②网络进程

③渲染进程

2.2、渲染主线程的工作原理

渲染主线程的消息队列

三、事件循环

3.1、什么是事件循环(Event Loop)

3.2、渲染主线程的事件循环如何确定任务的优先级? 

3.3、事件循环的执行示例

示例一

示例二

测试题:如下代码块执行后输出顺序是什么?

四、相关问题

4.1、为什么要使用事件循环

4.2、如何理解JS的异步

4.3、JS中计时器能精确计时吗,为什么?

五、总结与相关资源


事件循环(消息循环):引入

        事件循环是浏览器的核心内容。

        与计时器、Promise、ajax、node等技术有关。

        要想说清楚事件循环,必须先聊进程与线程。

一、浏览器的进程模型

1.1、什么是进程(Process)

        我们先看看定义: 

  • 进程是程序的执行实例。它是操作系统进行资源分配和调度的一个独立单位。
  • 进程拥有独立的内存空间,可以拥有或分配不同的资源如CPU时间、文件、消息队列等。
  • 进程可以创建子进程,形成进程树结构。

        对于coder来说,说到实例肯定不陌生,一个程序的运行就至少需要产生一个实例,实例负责给程勋运行提供运行所需的资源。

        简单的说,程序运行需要它专属的内存空间(RAM和虚拟内存),这部分内存空间可以简单的理解为该程序对应的进程。

        每个应用至少有一个进程,且相互独立,即使要通信,也要双方同意。

1.2、什么是线程(Thread)

        先看定义:

  • 线程是程序执行的逻辑单元,是程序中一个单一的顺序控制流程。
  • 在一个进程中可以包含多个线程,它们共享进程的资源,如内存空间,但每个线程有自己的线程栈和程序计数器。

        简单的说,线程是进程的执行者。一个进程可以有多个线程,线程之间资源共享, 通信简单,独立执行,开销较小。

        一个进程至少有一个线程,所以进程开启后就会自动创建一个线程来运行代码,该线程称之为主线程。

        如果程序需要同时执行多块代码,主线程就会启动更多的线程来执行代码,所以一个进程中可以包含多个线程。重要的事情要多次重复,这些线程资源共享, 通信简单,独立执行,开销较小(线程相比于进程)!

1.3、进程与线程之间的关系联系与区别

        综上所述,二者之间的联系与区别就很明确了:

  1. 进程是程序某一部分或整体的运行实例,每个程序运行都至少需要一个进程。(但不一定只有一个,为了保证程序的稳定性,往往会有多个进程,一个进程崩溃不会导致整个程序崩溃)
  2. 线程是进程的执行者,每个进程都至少包含一个线程(即主线程)。
  3. 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中。因此同一进程内的线程可以共享进程的资源,如全局变量、文件句柄等。
  4. 与创建新进程相比,创建线程的开销较小,因为线程可以复用进程的资源。
  5.  由于线程共享同一地址空间,线程间的通信更简单,不需要复杂的进程间通信机制。

        关系示意图:

二、浏览器有哪些进程和线程

        首先,浏览器是一个多进程多线程的应用程序。

2.1、浏览器的主要进程

        浏览器内部工作极其复杂,为了避免相互影响,为了减少连环崩溃的几率,当启动浏览器后,它会自动启动多个进程。一般主要有浏览器进程、网络进程、渲染进程

        可以在浏览器的任务管理器中查看当前的所有进程。

①浏览器进程

        主要负责界⾯显示、⽤户交互、子进程管理等。浏览器进程内部会启动多个线程处理不同的任务。

②网络进程

        负责加载网络资源。网络进程内部会启动多个线程来处理不同的⽹络任务。

③渲染进程

        渲染进程启动后,会开启⼀个渲染主线程主线程负责执行 HTML、CSS、JS 代码

        默认情况下,浏览器会为每个标签页开启⼀个新的渲染进程,以保证不同的标签页之间不相互影响。

2.2、渲染主线程的工作原理

        渲染主线程是浏览器中最繁忙的线程,也是前端开发中提高运行效率需要着重关注的线程,需要它处理的任务包括不限于:解析HTML、解析CSS、计算样式、布局、处理图层、每秒60次渲染,执行全局JS代码、执行事件处理函数、执行计时器回调函数等。

        那渲染主线程如何执行和调度这些任务呢?总要有个章法去有序执行这些步骤,同时兼顾这些步骤的因果顺序和中途插入的步骤。

        比如任务之间存在因果顺序:不解析HTML、CSS,就没办法执行布局任务。

        又比如任务之间会有插入情况:执行JS函数的过程中,用户点击了某个按钮或者计时器到了时间需要执行回调函数。

        这里就引入了一个概念:

        渲染主线程的消息队列
  1. 在最开始的时候,渲染主线程会进入一个无限循环
  2. 每一次循环会检查消息队列中是否有任务存在。如果有,就取出第一个任务执行,执行完一个后进入下一次循环;如果没有,则进入休眠状态。
  3. 其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时,如果主线程是休眠状态,则会将其唤醒以继续循环拿取任务

        简而言之,渲染主线程的消息队列就是渲染主线程的任务管家,负责给渲染主线程要执行的任务进行排序、管理、调度。渲染主线程只需要一直检查消息队列里面有没有任务,按序执行即可,但消息队列要考虑的可就多了(bushi)

        这样一来,就可以让每个任务有条不紊的、持续的进行下去了。现在就能引出本文的核心内容:事件循环。

三、事件循环

3.1、什么是事件循环(Event Loop)

        又称消息循环(Message Loop),在有些情景也叫 Run Loop。

        一言以蔽之:事件循环就是渲染主线程不断循环不断从消息队列中读取事件并执行的过程。

        也可以说:事件循环又叫做消息循环,是浏览器渲染主线程的工作方式。(但并非只有浏览器的渲染主线程会进行事件循环,有时候网络线程也会)

        不是所有的线程都有事件循环,但是渲染主线程一般都有。

3.2、渲染主线程的事件循环如何确定任务的优先级? 

        首先,任务本身没有优先级,消息队列遵守先进先出的规则。

        但是消息队列有优先级。消息队列一般至少由三个队列:微队列、交互队列、延时队列构成,其分类和优先级规则如下:

        微队列 > 交互队列 > 延时队列

  • 微队列:用户存放需要最快执行的任务(一般由Promise和MutationObserver生成),优先级「最高」
  • 交互队列:用于存放用户操作后产生的事件处理任务,优先级「高」
  • 延时队列:用于存放计时器到达后的回调任务,优先级「中」

        这里的优先级是指事件循环过程中,高优先级的队列会“插队”放入队列。比如现在队列中微队列和延时队列各有一个事件,先读取微队列中的任务,执行后又产生了一个微队列任务和一个交互队列任务,那么下一个执行的是新产生的微队列任务,然后是新产生的交互队列任务,最后才是一开始的延时队列任务。

        如下图所示,消息队列大概是个这样的模型,只有微队列完全空掉才会执行交互队列中的任务,在同一类型的队列中才严格遵守“先进先出”的队列规则:

3.3、事件循环的执行示例

        请问如下几个例子的输出顺序是什么?

示例一
setTimeout(function () {console.log(1);
}, 0);function delay(duration) {var start = Date.now();while (Date.now() - start < duration) {}
}
delay(3000);
console.log(2);// 输出顺序为 2 、 1

(点击代码详情查看答案)

        解析:整体先作为一个任务①顺序执行。setTimeout生成一个新任务②,放到延时队列中(虽然计时为0,但是任务①还没执行完毕,所以哪怕计时到了也只能在队列等候执行)。delay函数将渲染主线程阻塞3秒,然后输出2,任务①执行完毕,通过事件循环执行任务②,输出1。

示例二
function a() {console.log(1);Promise.resolve().then(function () {console.log(2);});
}
setTimeout(function () {console.log(3);
}, 0);Promise.resolve().then(a);console.log(5);// 输出顺序为: 5 、 1 、 2 、 3

(点击代码详情查看答案)

        解析:整体作为任务①执行。setTimeout生成一个新任务②,放到延时队列中。Promise生成一个新任务③(执行a函数),放到微队列中。然后输出5,任务①执行完毕。

        此时消息队列中微队列有任务③,优先执行,先输出1,然后Promise生成一个新任务④,放到微队列中,任务③执行完毕。

        此时微队列又有任务④,优先执行,输出2。任务④执行完毕。

        此时消息队列中微队列和交互队列为空,执行延时队列中的任务②,输出3,任务②执行完毕。

        即输出结果为:5 1 2 3。

测试题:如下代码块执行后输出顺序是什么?
function a() {console.log(1);Promise.resolve().then(function () {console.log(2);});
}setTimeout(function () {console.log(3);Promise.resolve().then(a)
}, 0);Promise.resolve().then(function(){console.log(4);
});console.log(5);// 输出顺序为: 5 、 4 、 3 、 1 、 2

四、相关问题

4.1、为什么要使用事件循环

        在本文2.2中提到“让每个任务有条不紊的、持续的进行下去”。那么为什么不使用事件循环就会出现问题?为什么“执行JS函数的过程中,用户点击了某个按钮或者计时器到了时间需要执行回调函数”就会有矛盾?这两个任务又没有因果关系,直接一起执行不行吗?

        事实上,JS是一门单线程语言,这是因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。也就是说,JS函数不能多个一起进行,哪怕两个任务相互独立,也要有个规定来调度任务,有序执行。所以必须要有一个像事件循环一样的逻辑来管理、调度任务。

4.2、如何理解JS的异步

        代码在执行过程中,会遇到一些无法立即处理的任务,比如:

  • 计时完成后需要执行的任务 —— setTimeout、setInterval
  • 网络通信完成后需要执行的任务 —— XHR、Fetch
  • 用户操作后需要执行的任务 —— addEventListener

        如果让渲染主线程等待这些任务的时机达到,就会导致渲染主线程长期处于“阻塞”的状态,从而让用户感觉浏览器“卡死”,让用户的体验变差。

        因此,浏览器使用异步来解决这个问题。

        具体做法是当某些任务发生时,比如计时器、网络、事件监听,主线程将任务交给其他线程去处理,自身立即结束任务的执行,转而执行后续代码。当其他线程完成时,将事先传递的回调函数包装成任务,加入到消息队列的末尾排队,等待主线程调度执行。

        从而最大程度的保证单线程的流畅运行。

4.3、JS中计时器能精确计时吗,为什么?

        不可以。原因如下:

        从硬件角度来说:JS计时器是调用了操作系统中的计时函数,该函数本身就有少量偏差,硬件精度有限。

        从语法标准上说:W3C标准中建议浏览器的计时器嵌套层级超过5层,则存在至少4ms的最少事件,这样也会带来偏差。

// 例如嵌套的层数小于等于5层,那么就会按照设置的时间执行。
setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {}, 0);}, 0);}, 0);}, 0);
}, 0);// 假如嵌套的层数大于5层,即使设置了0毫秒的间隔,浏览器也会确保至少有4毫秒的延迟,以避免潜在的性能问题,即:setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {}, 0);}, 0);}, 0);}, 0);}, 0);}, 0);// 实际执行效果:setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {}, 4);}, 4);}, 4);}, 4);}, 4);}, 4);

        从事件循环的逻辑上讲,计时器的回调函数只能在主线程空闲时进行,并不一定能在计时完成后立马开始执行逻辑。

        综上所述,JS中计时器做不到精确计时。

五、总结与相关资源

        度一教育的袁进老师谈到他的理解:单线程是异步产生的原因,事件循环是异步的实现方式。

        本质是因为渲染进程因为计算机图形学的限制,只能是单线程。所以需要“异步”这个技术思想来解决页面阻塞的问题,而“事件循环”是实现“异步”这个技术思想的最主要的技术手段。

        但事件循环并不是全部的技术手段,比如Promise,虽然受事件循环管理,但是如果没有事件循环,单一Promise依然能实现异步不是吗?

        博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

        更多优质内容,请关注:

JS底层逻辑:

        路由通配符,小小的字符有大大的作用,你真的熟悉吗? 

        管理数据必备!侦听器watch用法详解

        什么是深拷贝?深拷贝和浅拷贝有什么区别

JS语法篇:

        你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

        对象数据的读取,看这一篇就够了!

        通过array.every()实现数据验证、权限检查和一致性检查,array.some与array.every的区别

        通过array.some()实现权限检查、表单验证、库存管理、内容审查和数据处理

        通过array.map()实现数据转换、创建派生数组、异步数据流处理、搜索和过滤等需求

        通过array.reduce()实现数据汇总、条件筛选和映射、对象属性的扁平化、转换数据格式等

        通过array.filter()实现数组的数据筛选、数据清洗和链式调用

巧妙算法与窍门:

        多维数组操作,不要再用遍历循环foreach了,来试试数组展平的小妙招!

        别再用双层遍历循环来做新旧数组对比,寻找新增元素了!

        shpfile转GeoJSON且控制转化精度;如何获取GeoJSON?GeoJson结构详解

        Mapbox添加行政区矢量图层、分级设色图层、自定义鼠标悬浮框、添加天地图底图等

Element plus拓展:

        通过el-tree自定义渲染网页版工作目录,实现鼠标悬浮显示完整名称等

        el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

        el-table中如何添加渐变色带、多色色带

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/38827.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ctfshow sqli-libs web561--web568

web561 ?id-1 or 1--?id-1 union select 1,2,3--?id-1 union select 1,(select group_concat(column_name) from information_schema.columns where table_nameflags),3-- Your Username is : id,flag4s?id-1 union select 1,(select group_concat(flag4s) from ctfshow.f…

扩展学习|风险评估和风险管理:回顾其基础上的最新进展

文献来源&#xff1a;[1]Aven, T. (2016). Risk assessment and risk management: Review of recent advances on their foundation. European journal of operational research, 253(1), 1-13. 文章简介&#xff1a;大约30-40年前&#xff0c;风险评估和管理被确立为一个科学领…

数据结构 - C/C++ - 链表

目录 结构特性 内存布局 结构样式 结构拓展 单链表 结构定义 节点关联 插入节点 删除节点 常见操作 双链表 环链表 结构容器 结构设计 结构特性 线性结构的存储方式 顺序存储 - 数组 链式存储 - 链表 线性结构的链式存储是通过任意的存储单元来存储线性…

技术分享:分布式数据库DNS服务器的架构思路

DNS是企业数字化转型的基石。伴随微服务或单元化部署的推广&#xff0c;许多用户也开始采用分布式数据库将原来的单体数据库集群服务架构拆分为大量分布式子服务集群&#xff0c;对应不同的微服务或服务单元。本文将从分布式数据库DNS服务器的架构需求、架构分析两方面入手&…

湖北大学2024年成人高考函授报名专升本市场营销专业介绍

在璀璨的学术殿堂中&#xff0c;湖北大学如同一颗璀璨的明珠&#xff0c;熠熠生辉。为了满足广大社会人士对于继续深造、提升自我、实现职业梦想的渴望&#xff0c;湖北大学特别开设了成人高等继续教育项目&#xff0c;为广大有志之士敞开了一扇通往知识殿堂的大门。 而今&…

【FFmpeg】av_write_frame函数

目录 1.av_write_frame1.1 写入pkt&#xff08;write_packets_common&#xff09;1.1.1 检查pkt的信息&#xff08;check_packet&#xff09;1.1.2 准备输入的pkt&#xff08;prepare_input_packet&#xff09;1.1.3 检查码流&#xff08;check_bitstream&#xff09;1.1.4 写入…

【创建者模式-建造者模式】

概要 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 建造者模式包含以下角色 抽象建造者类&#xff08;Builder&#xff09;&#xff1a;这个接口规定要实现复杂对象的那些部分的创建&#xff0c;并不涉及具体的部件对象的创建。具体建…

在WSL Ubuntu中启用root用户的SSH服务

在 Ubuntu 中&#xff0c;默认情况下 root 用户是禁用 SSH 登录的&#xff0c;这是为了增加系统安全性。 一、修改配置 找到 PermitRootLogin 行&#xff1a;在文件中找到 PermitRootLogin 配置项。默认情况下&#xff0c;它通常被设置为 PermitRootLogin prohibit-password 或…

一篇文章学会【node.js安装以及Vue-Cli脚手架搭建】

一.为什么搭建Vue-Cli (1).传统的前端项目结构&#xff1a; 一个项目中有许多html文件&#xff0c;每一个html文件都是相互独立的&#xff0c; 如果需要在页面中导入一些外部依赖的组件&#xff0c;就需要在每一个html文件中都需要导入&#xff0c;非常麻烦 (2).现在的前端…

A股低开高走,近3000点,行情要启动了吗?

A股低开高走&#xff0c;近3000点&#xff0c;行情要启动了吗&#xff1f; 今天的A股&#xff0c;让人瞪目结舌了&#xff0c;你们知道是为什么吗&#xff1f;盘面上出现2个重要信号&#xff0c;一起来看看&#xff1a; 1、今天两市低开高走&#xff0c;银行板块护盘指数&…

盘古5.0,靠什么去解最难的题?

文&#xff5c;周效敬 编&#xff5c;王一粟 当大模型的竞争开始拼落地&#xff0c;商业化在B端和C端都展开了自由生长。 在B端&#xff0c;借助云计算向千行万业扎根&#xff1b;在C端&#xff0c;通过软件App和智能终端快速迭代。 在华为&#xff0c;这家曾经以通信行业起…

Error: A JNl error has occurred, please check your installation and try again.

Eclipse 运行main方法的时候报错&#xff1a;Error: A JNl error has occurred, please check your installation and try again. 一、问题分析 导致这个问题&#xff0c;主要原因&#xff0c;我认为是在新版本中&#xff0c;默认的JDK编译版本与我们配置的JDK版本不一致导致的…

公网环境使用Potplayer远程访问家中群晖NAS搭建的WebDAV听歌看电影

文章目录 前言1 使用环境要求&#xff1a;2 配置webdav3 测试局域网使用potplayer访问webdav4 内网穿透&#xff0c;映射至公网5 使用固定地址在potplayer访问webdav 前言 本文主要介绍如何在Windows设备使用potplayer播放器远程访问本地局域网的群晖NAS中的影视资源&#xff…

告别流失,拥抱增长!Xinstall智能邀请系统,让你的App拉新更高效

在移动互联网时代&#xff0c;App的推广和运营面临着诸多挑战。其中&#xff0c;如何有效地进行邀请拉新活动&#xff0c;吸引更多新用户&#xff0c;成为了每个运营者都需要面对的问题。今天&#xff0c;我们将为大家介绍一款能够帮助你轻松解决这一难题的神器——Xinstall。 …

互联网框架五层模型详解

注&#xff1a;机翻&#xff0c;未校对。 What is the Five Layers Model? The Framework of the Internet Explained 五层模型互联网框架解释 Computer Networks are a beautiful, amazing topic. Networks involve so much knowledge from different fields, from physics…

[数据集][目标检测]城市街道井盖破损未盖丢失检测数据集VOC+YOLO格式4404张5类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4404 标注数量(xml文件个数)&#xff1a;4404 标注数量(txt文件个数)&#xff1a;4404 标注…

昇思25天学习打卡营第03天 | 张量 Tensor

昇思25天学习打卡营第03天 | 张量 Tensor 文章目录 昇思25天学习打卡营第03天 | 张量 Tensor张量张量的创建张量的属性Tensor与NumPy转换稀疏张量CSRTensorCOOTensor 总结打卡 张量 张量&#xff08;Tensor&#xff09;是一种类似于数组和矩阵的特殊数据结构&#xff0c;是神经…

MATLAB|更改绘图窗口的大小和位置

MATLAB绘图 plot、plot3、cdfplot都适用 效果 如下图&#xff0c;运行程序后可以直接得到这两个绘图窗口。 右上角的Figure1是原始图片&#xff0c;右下角的Figure2是调整了位置和大小后的绘图窗口。 完整源代码 % 绘图大小和位置调整 % Evand©2024 % 2024-7-1/Ver1…

Transformer模型原理细节解析

基本原理: Transformer 的核心概念是 自注意力机制(Self-Attention Mechanism),它允许模型在处理每个输入时“关注”输入序列的不同部分。这种机制让模型能够理解每个单词或符号与其他单词或符号之间的关系,而不是逐个地线性处理输入。 Transformer 主要由两个部分组成:…

手把手教你搭建PyTorch环境:MindStudio中PyTorch模型开发实战

本次实验的视频链接如下&#xff1a;​https://www.bilibili.com/video/BV1iA4y1f7o1/ 本次实验在MindStudio上进行&#xff0c;请先按照 教程 配置环境,安装MindStudio。 ​ MindStudio的是一套基于华为自研昇腾AI处理器开发的AI全栈开发工具平台&#xff0c;该IDE上功能很多…