进程和线程
在编程语言中,线程和进程是非常重要的两个概念
进程
一个正在内存中运行的程序就表示一个进程。例如,你可以同时开启2个qq,这时候在内存中就会有两个QQ程序在运行。 在windows中我们可以通过 查看进程 工具来 观察内存中正在运行的程序有哪些
线程
进程就像一个工厂,而线程就是组成这个工程的一个个流水线车间。每个车间都有自己的功能。有些车间的存在是必须的,缺少了它工厂就会停工。但是有些车间,它的存在只是为了提高生产效率,就算把它砍掉。工厂还是可以继续生产,只是速度可能会变慢。一般情况下,一个工厂至少有一条生产线。
从程序的角度来说,一个进程是由一个或者多个线程组成。为了保证程序能够正常运行,一个进程至少有一条线程,这条线程我们称之为 主线程
cpu与线程之间的关系
程序是运行在内存当中的,但是程序的计算是由CPU来决定的。CPU给每个线程分配执行的时间片段,执行完时间片段之后就会切换到另一个线程。
单线程
一个程序有且仅有一条线程,我们称之为单线程程序
多线程
当一个程序除了有一条主线程之外,还有其他线程进行辅佐。那么我们就称之为多线程程序。
多线程的好处
当一个程序中,有非常多的IO操作,或者非常密集的计算操作的情况。例如: 读取磁盘中文件和网络请求。这些都是属于比较耗时的操作。当我们将这些操作都放置在主线程中执行的时候,就会引起主线程的阻塞。给程序使用者带来非常不好的体验。
例如,我们的一个app中有许多的图片要加载,如果要等这些图片都加载完成再让用户去操作。那么用户是不会有兴趣再打开你的APP第二次的。
如果我们将这些操作放到 主线程之外的其它线程中去执行。那么用户可以在资源还没完全加载的情况下对你的APP进行操作。提高用户的体验
JS线程
javascript是单线程语言。当在主线程中遇到较为耗时的操作的时候,就会导致线程阻塞。
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="startBTN">开始计算斐波那契数列</button>
<button id="addOneBtn">+1</button>
<span id="result">0</span>
</body>
</html>
<script>
var startBTN = document.getElementById("startBTN");
var addOneBtn = document.getElementById("addOneBtn");
var result = document.getElementById("result");
var num = 0;
//通过递归计算斐波那契数列
function fn(n){
if(n===1 || n===2){
return 1
}
return fn(n-1)+fn(n-2)
}
startBTN.onclick = function(){
var rs = fn(43);
console.log(rs);
this.innerText = rs;
}
addOneBtn.onclick = function(){
result.innerText = ++num;
}
</script>
代码执行结果下图: 当我们一开始点击 +1 按钮的时候,它可以快速地给我们反馈结果。 但是当我们点击 计算斐波那契数列按钮之后,再回来点击 +1 按钮,发现 是点击不了的。 这就是由于 计算43斐波那契数列的比较耗时。程序的执行栈只能等计算结束之后才能够执行下一个操作。 这就是 线程阻塞。
JS线程阻塞
线程阻塞
web worker
为了解决js在主线程中执行耗时操作时,导致主线程的阻塞。我们可以通过浏览器提供的 webworker 功能来实现多线程操作。 这里需要注意的是,web worker只是浏览器提供的一项功能,它可以让一个JS程序以独立的方式运行在后台。但它本身并没有改变JS是单线程语言的特点。 在web worker里不能去改变 DOM
使用web worker
- 创建一个需要单独执行的JS文件。 xxx.js
this.onmessage = function (e) {
console.log('work接收到的数据为:', e.data);
this.postMessage("你好,我是worker发来的数据")
}
- 在html文件中 通过 new Worker('xxx.js')的方式来创建线程, 注意,这里不需要导入xxx.js到html中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="startWorker()">启动子线程</button>
<button onclick="stopWorker()">停止子线程</button>
</body>
</html>
<script>
var w;
function startWorker() {
//1. 判断浏览器是否支持 web worker
if (typeof (Worker) !== "undefined") {
// 2. 判断w是否为空
if (typeof (w) == "undefined") {
// 2.1 创建一条web worker线程
w = new Worker("webworker.js");
}
//3. 给子线程发送消息
w.postMessage("您好,我是主线程");
//4. 监听子线程的onmessage方法
w.onmessage = function (event) {
console.log(event.data);
};
} else {
console.log("很抱歉,您的浏览器不支持 web worker");
}
}
function stopWorker() {
//关闭线程
w.terminate();
}
</script>
程序运行结果
注意 为了使webworker生效,我们需要将页面运行在 服务器下
我使用的是 vscode,可以安装插件 Live Server,然后在html文件中,通过右键选择
Worker()
使用Worker构造函数创建 线程对象
var wk = new Worker('文件路径')
postMessage()
线程之间需要进行通信,其中一个就是发送消息。线程可以通过postMessage()函数向其他线程发送消息
onMessage()
监听线程之间的通信,当有其他线程向当前线程发送消息,可以在onMessage()函数中进行响应
terminate()
主线程中终止worker,此后无法再利用其进行消息传递。注意:一旦terminate后,无法重新启用,只能另外创建。
应用场景
轮询
当服务器端有数据更新,我们想让前端页面也自动更新。这时候我们就需要对服务器的数据进行监听,而监听的方法就是不停地向服务器发起请求。当请求到的数据和本地缓存的数据不一致的时候,就通知页面进行更新。