线程,是程序执行流的最小单位。线程可与同属一个进程的其他线程共享所拥有的全部资源,同一进程中的多个线程之间可以并发执行。线程有就绪,阻塞,运行三种基本状态。
阮一峰大神针对进程和线程的类比,很是形象:计算机的核心CPU,是个工厂,时刻运转着,工厂里有很多个车间(进程),一个车间开工其他车间不能开工,就是说:单核CPU一次只能运行一个进程。任何时候,CPU总是运行一个进程。
而一个车间里面有很多工人(线程),协同完成一个任务。所以:一个进程可以包括多个线程。车间空间是工人共享的,一个进程中的内存空间是所有线程共享的。
但是房间大小不同个,容纳量不同,有些线程进去了,其他线程就不能再进去使用了。这就是:一个线程使用某些共享内存时,其他线程得等它结束后才能使用共享内存。
内存可以上锁,防止其他线程进来,【互斥锁】也就是防止多个线程读写同一块内存区域。
还有些共享内存比较大,可以容纳多个线程,但是也是有限的,只能容纳固定数目的线程使用。如何保证多个线程不冲突呢?内存门口有个钥匙架,如果发现钥匙架空了,那就不能再进去了【信号量】。
事件循环(Event Loop):
JavaScript是单线程的,单线程意味着需要一个任务队列来管理任务事件,任务分为两种:同步任务和异步任务。同步任务是在主线程上面排队执行的任务,只有前面任务完成,下一个任务才能执行。异步任务是不进入主线程,而是进入任务队列中,只有通知主线程某个异步任务可以执行了,该任务方可执行。
同步任务在主线程执行形成一个执行栈,主线程之外有个任务队列(鼠标点击事件,键盘点击事件,网络请求),只要异步任务触发,在任务队列中放置一个事件,等执行栈中同步任务完成后,系统才会读取任务队列执行异步任务。主线程会重复去任务队列中读取任务并执行——事件循环。
任务队列是“先进先出”的数据结构,先来的优先被主线程读取。
主线程运行产生堆heap和栈stack,栈里面是各种同步方法,栈里面代码执行完毕主线程会读取任务队列,依次执行事件的回调函数。
所以,栈里面的同步任务总是在任务队列之前执行。
看个定时器的例子:
console.log(1); setTimeout(function(){console.log(2);},0); console.log(3);
定时器里面的第二个参数延迟时间设置为0或者比0小的数,输入都是1,3,2。因为console.log(1)和console.log(3)在主线程执行,而定时器是个异步任务放在了任务队列里面,只有栈中的同步事件清空后才能执行任务队列中的回调函数。
setTimeout(fn,0)意思是:fn在主线程最早可得的空闲事件执行,但是也得等主线程的同步任务和任务列表中排在它前面的事件都处理完才能执行它。
要注意的点是:setTimeout里面指定的延迟时间执行并不能保证就是那个时间执行,必须等到排在它之前的任务队列中的回调函数执行完毕后才能执行它。
————————————————————————————————————————————————
JavaScript是单线程,H5中的Web Worker作用就是给js创造多线程环境,允许主线程创建Worker线程,将一些任务分配给Worker运行,主线程运行同时,Worker线程在后台运行,互不干扰。等Worker线程完成计算任务,再把结果返回给主线程。主线程(通常负责UI交互)就不用被异步队列所打扰,运行就比较流畅。
使用Web Worker要注意的点:
1,同源限制:分配给Worker线程执行的脚本必须与主线程脚本同源。
2,DOM限制:Worker无法读取ODM对象。
3,通信限制:Worker与主线程不在一个上下文环境,不能直接通信。
4,脚本限制:不能执行alert,confirm但是可以使用XHR发送Ajax请求。
5,文件限制:无法读取本地文件,加载的脚本必须来源于网络。
Worker线程使用完毕后,为了节省资源必须关闭Worker。
Web Worker有自己的全局对象,不是主线程的window对象,例如:self.name,self.onmessage等。
Worker的应用场景:轮询。
这样看来Web Worker其实就是主线程的一个子线程,帮忙处理一些任务队列里面的事件,分担主线程的任务压力。
【完】
只有不悲不喜的人,能当得起大喜大悲。也只有无所谓得失,不等待回音的人,能攀上人生的高峰。