zoukankan      html  css  js  c++  java
  • 进程和线程,同步和异步

    进程与线程

    一个程序中至少有一个进程,而一个进程中至少有一个线程

    • 进程是运行中的程序,线程是进程内部的一个执行序列
    • 进程是资源分配的单元,线程是执行单元
    • 进程间切换代价大,线程间切换代价小
    • 进程拥有的资源多,线程拥有的资源少
    • 多个线程共享进程的资源

    如:

    工厂的资源 -> 系统分配的内存(独立的一块内存)

    工厂之间的相互独立 -> 进程之间相互独立

    多个工人协作完成任务 -> 多个线程在进程中协作完成任务

    工厂内有一个或多个工人 -> 一个进程由一个或多个线程组成

    工人之间共享空间 -> 同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)

    浏览器是多进程的!!!

    浏览器的进程:

    • Browser进程:浏览器的主进程(负责协调、主控),只有一个
      • 负责浏览器界面显示,与用户交互。如前进后退
      • 负责各个页面的管理,创建和销毁其他进程
      • 将Renderer进程得到的内存中的Bitmap,会知道用户界面上
      • 网络资源的管理,下载等
    • 第三方插件进程:每种类型的插件对应一个进程,仅当使用插件时才创建
    • GPU进程:最多一个,用于3D绘制等
    • 浏览器渲染进程(浏览器内核)(Renderer进程内部是多线程的):默认每个Tab页面一个进程,互不影响。
      • 页面渲染、脚本执行、事件处理

    重点:浏览器内核(渲染进程)

    渲染进程是多线程的

    包含:

    • GUI渲染线程
      • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
      • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
      • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行
    • JS引擎线程
      • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
      • JS引擎线程负责解析Javascript脚本,运行代码。
      • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
      • 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
    • 事件触发线程
      • 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
      • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
      • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
      • 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
    • 定时器触发线程
      • 传说中的setInterval与setTimeout所在线程
      • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
      • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
      • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
    • 异步http请求线程
      • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
      • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

    单线程与多线程

    单线程

    所谓单线程(主线程),指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。

    多线程

    如浏览器的渲染进程:

    • GUI渲染线程
    • JS引擎线程
    • 事件触发线程
    • 定时器触发线程
    • 异步http请求线程

    同步和异步

    同步:如果在函数返回的时候,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果),那么这个函数就是同步的

    例如:

    console.log('我是第一件事');
    console.log('我是第二件事');
    

    1.这两个函数都是同步的

    2.这段代码是同步的

    3.若有一个函数执行时间很长,后面的函数只能等待这个函数执行完才能执行(同步是阻塞的)

    异步

    先来看段代码:

    console.log('我是第一件事');
    setTimeout(function () {
      console.log('我突然有事,晚点再做第二件事');
    },1000)
    console.log('我是第三件事');
    
    //我是第一件事
    //我是第三件事
    //我突然有事,晚点再做第二件事
    

    这段代码实现的就是异步

    1.执行console.log('我是第一件事')

    2.setTimeout异步函数跳过

    3.执行console.log('我是第三件事')

    4.js引擎空闲1秒后将异步函数推入事件队列-->执行console.log('我突然有事,晚点再做第二件事')

    补充:setTimeout和setInterval接受两个参数,第一个参数为函数。第二个为时间(毫秒),及js引擎空闲几秒后将其推入事件队列。

    再来一个:

    console.log(0,'第一');
    for (let i = 0;i<3;i++){
    	setTimeout(function(){
    		console.log(i,'第三');
    	},2000)
        console.log(i,'第二');
    };
    
    //0 "第一"
    //0 "第二"
    //1 "第二"
    //2 "第二"
    //两秒之后
    //0 "第三"
    //1 "第三"
    //2 "第三"
    

    补充:

    • 异步机制是浏览器的两个或两个以上的常驻线程共同完成的
    • 如:异步请求是由两个常驻线程:JS执行线程和事件触发线程共同完成的,JS的执行线程发起异步请求(这时浏览器会开一条新的HTTP请求线程来执行请求,这时JS的任务已完成,继续执行线程队列中剩下的其他任务),然后在未来的某一时刻事件触发线程监视到之前的发起的HTTP请求已完成,它就会把完成事件插入到JS执行队列的尾部等待JS处理。
    • 如:定时触发(settimeout和setinterval)是由浏览器的定时器线程执行的定时计数,然后在定时时间把定时处理函数的执行请求插入到JS执行队列的尾端

    消息队列与事件循环

    如上图所示:

    • 左边的栈存储的是同步任务,就是那些能立即执行、不耗时的任务,如变量和函数的初始化、事件的绑定等等那些不需要回调函数的操作都可归为这一类。

    • 右边的堆用来存储声明的变量、对象。

    • 下面的队列就是消息队列,一旦某个异步任务有了响应就会被推入队列中。如用户的点击事件、浏览器收到服务的响应和setTimeout中待执行的事件,每个异步任务都和回调函数相关联。

    • JS引擎线程用来执行栈中的同步任务,当所有同步任务执行完毕后,栈被清空,然后读取消息队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务。

    • JS引擎线程从消息队列中读取任务是不断循环的,每次栈被清空后,都会在消息队列中读取新的任务,如果没有新的任务,就会等待,直到有新的任务,这就叫事件循环。

    以Ajax异步请求为例:

    实例

    最后来个大的:

    setTimeout(function(){
        for(var i = 0; i < 100000000; i++){}
        console.log('timer a');
    }, 0)
    
    for(var j = 0; j < 5; j++){
        console.log(j);
    }
    
    setTimeout(function(){
        console.log('timer b');
    }, 0)
    
    function waitFiveSeconds(){
        var now = (new Date()).getTime();
        while(((new Date()).getTime() - now) < 5000){}
        console.log('finished waiting');
    }
    
    document.addEventListener('click', function(){
        console.log('click');
    })
    
    console.log('click begin');
    waitFiveSeconds();
    
    //0
    //1
    //2
    //3
    //4
    //click begin
    //finished waiting
    //timer a
    //timer b
    //click
    

    再说一下:setTimeout(fn,0);是js引擎空闲后立即插入队列,并不是立即执行

    1.第一个setTimeout异步函数跳过

    2.执行for循环输出0,1,2,3,4

    3.第二个setTimeout异步函数跳过

    4.执行console.log('click begin')输出 click begin

    5.调用waitFiveSeconds()函数输出finished waiting

    6.waitFiveSeconds()执行完后同步任务结束,js引擎空闲会一次将异步函数插入队列

    7.输出timer a;timer b;click

    参考文章1
    参考文章2

  • 相关阅读:
    他扎根农村种植双孢菇,从门外汉成为了“土专家”
    农村小伙成立生态农业基地,带领村民打开致富新门路
    小伙让蜜柚成为真正的“黄金柚”,帮助600多农户发家致富
    js时间格式化函数,支持Unix时间戳
    js时间格式化函数,支持Unix时间戳
    js时间格式化函数,支持Unix时间戳
    js时间格式化函数,支持Unix时间戳
    Netty随记之ChannelInboundHandlerAdapter、SimpleChannelInboundHandler
    Netty随记之ChannelInboundHandlerAdapter、SimpleChannelInboundHandler
    Netty随记之ChannelInboundHandlerAdapter、SimpleChannelInboundHandler
  • 原文地址:https://www.cnblogs.com/loveyt/p/9621702.html
Copyright © 2011-2022 走看看