进程和线程

在编程语言中,线程和进程是非常重要的两个概念

进程

一个正在内存中运行的程序就表示一个进程。例如,你可以同时开启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

  1. 创建一个需要单独执行的JS文件。 xxx.js
this.onmessage = function (e) {
  console.log('work接收到的数据为:', e.data);
  this.postMessage("你好,我是worker发来的数据")
}
  1. 在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后,无法重新启用,只能另外创建。

应用场景

轮询

当服务器端有数据更新,我们想让前端页面也自动更新。这时候我们就需要对服务器的数据进行监听,而监听的方法就是不停地向服务器发起请求。当请求到的数据和本地缓存的数据不一致的时候,就通知页面进行更新。

results matching ""

    No results matching ""