1. 操作系统基础概念
-
程序:
Program, 可供CPU执行的代码, 存储在外存中, Ex:.exe-- 静止的 -
进程:
Progress, 把程序从外存中调入内存, 分配必需数据和可执行代码段 -> 进程是程序的可执行状态 -- 活动的 -
线程:
Thread, 线程是进程内部执行代码的基本单位 -
-
试题: 进程和线程间的关系
-
进程是操作系统分配内存的基本单位
-
线程是
CPU执行代码的基本单位 -
线程处于进程内部, 一个进程中至少有一个线程, 也可以同时存在多个线程
-
每个线程也需要自己独立的内存空间, 至少 2
MB -
操作系统中多个线程在宏观上看是 "同时执行", 微观上看是 "依次循环执行" -- 并发执行
-
-
试题:
chrome浏览器中的线程模型是怎样的?-
发起
HTTP请求使用 6 个并发线程 -- 请求线程 -
执行所有代码(
HTML/CSS/JS...... ) 使用一个线程 --UI渲染主线程
-
-
页面中执行一段耗时的
JS任务, 出现的现象<button>按钮1</button> <script type="text/javascript" src="xxx.js"></script> <button>按钮2</button>- 若
xxx.js不退出, 则按钮 2 不可见, 按钮 1 可见但没有事件处理 - 产生原因: 浏览器中执行代码只使用 1 个
UI主线程 - 解决方法: 创建一个并发线程, 让它去执行耗时的
JS任务,UI主线程仅负责页面渲染和事件监听
- 若
2. Web Worker
2.1. 创建新的执行线程的方法
- 语法:
new Worker('xxx.js')
2.2. Worker 线程致命的缺陷
- 浏览器不允许
Web Worker线程使用任务DOM&BOM对象 -> 浏览器只允许UI主线程使用DOM&BOM对象
2.3. 让 Worker 线程给 UI 主线程发消息
-
UI主线程:let myWorker = new Worker('xxx.js') myWorker.addEventListener('message', function (e) { e.data }) // 还可以写成 myWorker.onmessage = function (e) { e.data } -
Worker线程...... postMessage('stringMsg') -
Ex:
<body> <h3>Worker 线程给 UI 主线程发消息</h3> <button class="btnOne">按钮1</button> <script type="text/javascript"> let myWorker = new Worker('workerTest.js') myWorker.addEventListener('message', function (e) { console.log(`UI 主线程接受到消息:${e.data}`) }) </script> <button class="btnTwo">按钮2</button> </body>// workTest.js /* Worker 线程给 UI 主线程发消息 */ console.time('质数计算') let num = 9999999999999 let result = isPrime(num) console.timeEnd('质数计算') postMessage(`${num}是质数吗? ${result}`) function isPrime(num) { // 模拟出耗时 5 s+ 的效果 let start = new Date().getTime() do { var now = new Date().getTime() } while(5000 >= now - start) // JS 中的最大整数只能到十位 num = parseInt(num) for(var i = 2;i < num; i++) { if (0 === num % i) { break } } if (i < num) { return false } else { return true } }
2.4. 让 UI 主线程给 Worker 线程发消息
-
UI主线程let myWorker = new Worker('xxx.js') myWorker.postMessage('stringMsg') -
Worker线程...... onmessage = function (e) { e.data } -
Ex:
<style> span.isPrimeBtn{ display: inline-block; padding: 0 15px; height: 25px; line-height: 25px; background: #2F4056; color: #fff; cursor: pointer; } </style> <body> <h3>UI 主线程给 Worker 线程发消息</h3> <button class="btnOne">按钮1</button> <input type="text" name="txtNumber" /> <span class="isPrimeBtn">开始质数判断</span> <button class="btnTwo">按钮2</button> <script type="text/javascript"> let isPrimeBtn = document.body.getElementsByClassName('isPrimeBtn')[0] isPrimeBtn.addEventListener('click', function () { let num = document.getElementsByName('txtNumber')[0].value // 创建 Worker 线程执行耗时任务 let myWorker = new Worker('workerMsg.js') myWorker.postMessage(num) }) </script> </body>// workerMsg.js /* Worker 线程给 UI 主线程发消息 */ onmessage = function (e) { let num = e.data if (!num) { console.log('请输入值后在判断!') return false } console.time('质数计算') let result = isPrime(num) console.timeEnd('质数计算') console.log(`${num}是质数吗? ${result}`) } function isPrime(num) { // 模拟出耗时 5 s+ 的效果 let start = new Date().getTime() do { var now = new Date().getTime() } while(5000 >= now - start) // JS 中的最大整数只能到十位 num = parseInt(num) for(var i = 2;i < num; i++) { if (0 === num % i) { break } } if (i < num) { return false } else { return true } }
总结: 项目中使用
Worker的场景
- 页面中需要进行复杂的运算( Ex: 深度递归, 循环嵌套等 )导致很耗时, 在
UI主线程中执行会导致页面内容 "卡死", 可以使用Worker线程与UI主线程并发执行
-
Ex: 在页面中有两个输入框, 一个按钮, 点击按钮后对两个输入框中的数字进行整数运算( 假设此运算很耗时 ), 最后在一个
div中显示运算结果<body> <h3>使用 Worker 进行复杂运算</h3> 数字1: <input type="text" name="txtNumberOne" /> 数字2: <input type="text" name="txtNumberTwo" /> <input type="button" class="numComputeBtn" value="开始执行复杂运算" /> <div class="computeResult">0</div> <script type="text/javascript"> let numComputeBtn = document.body.getElementsByClassName('numComputeBtn')[0] numComputeBtn.addEventListener('click', function () { let numOneEl = document.getElementsByName('txtNumberOne')[0] let numTwoEl = document.getElementsByName('txtNumberTwo')[0] let numOneVal = numOneEl.value let numTwoVal = numTwoEl.value if (!numOneVal && numTwoVal) { alert('请输入数据后在计算!') return false } // 创建并执行 Worker 线程 let myWorker = new Worker('numCompute.js') // 发送消息 myWorker.postMessage(`${numOneVal},${numTwoVal}`) // 接受消息 myWorker.addEventListener('message', function (e) { let computeResult = document.body.getElementsByClassName('computeResult')[0] computeResult.innerHTML = e.data }) }) </script> </body>// numCompute.js // 等待接受 UI 主线程的消息 onmessage = function (e) { let arr = e.data.split(',') let numOne = parseInt(arr[0]) let numTwo = parseInt(arr[1]) // 执行运算 let result = numOne + numTwo // 将处理结果反传给 UI 主线程 postMessage(result) }
3. Web Socket
Socket: 插座, 套接字, 源自于 90+ 年代 C 语言, 所有的网络通讯底层都是基于套接字编程 套接字用于 "接受数据 & 发送数据"
3.1. HTTP 协议特点
- 属于 "请求-响应" 模型的协议, 必须客户端先发送请求, 服务器才会给出响应; 一个请求, 只能得到一个响应 有些应用场景, 此模型有缺陷: 实时走势, 聊天室 -- 即使客户端不发送请求, 服务器端只要有数据更新也应该立即给客户端
- 解决办法: 长轮询(
Long Polling) / 心跳请求: 定时器 +XHR- 该办法并不完美, 心跳过于频繁 -> 服务器压力太大; 不频繁 -> 数据时效性差
3.2. Web Socket 协议
属于 "广播-收听" 模式的协议, 只要客户端连接到服务器就不再断开( 永久连接 ), 双方建立 "全双工通信通道", 一方面不停的给对方发信息, 同时对方也可以发送 / 不发送信息 可以解决 "实时走势, 聊天室" 应用中的问题
3.3. 基于 Web Socket 协议的服务器端程序
java,PHP,Node.js都可以编写
3.4. 基于 Web Socket 协议的客户端程序
java,PHP,Node.js,HTML5都可以编写
3.5. 使用 Node.js 编写一个 Web Socket 协议的服务器端应用
-
下载第三方
Web Socket协议模块npm i ws -
编写
Web Socket服务端程序// 此 JS 演示 Wen Socket 服务端应用 const ws = require('ws') let server = new ws.Server({ port: 8090 }) server.on('connection', socket => { console.log('WS 服务器接收一个客户端连接......') // 套接字孔1: 从客户端接收消息 socket.on('message', msg => { console.log(`WS 服务器接收到消息: ${msg}`) }) // 套接字孔2: 向客户端发送消息 let counter = 1 let timer = null clearInterval(timer) timer = setInterval(() => { socket.send(`你好, 我是 WS 服务器 - ${counter}`) counter++ }, 1000) // 若连接已断开, 则不再继续发送消息 socket.on('close', () => { console.log('客户端连接已断开') clearInterval(timer) }) })
3.6. 使用 HTML5 编写一个 Web Socket 协议的客户端应用
<body>
<h3>使用 HTML5, 创建 Web Socket 协议的客户端程序</h3>
<button id="btnOne">连接到 WS 服务器</button>
<button id="btnTwo">开始接受 WS 服务器的消息</button>
<button id="btnThree">向服务器发送一条消息</button>
<button id="btnFour">断开 WS 连接</button>
<script type="text/javascript">
// 全局变量: 用于与 WS 服务器通信的套接字对象
let socket = null
// 创建到 WS 服务器连接
let btnOne = document.getElementById('btnOne')
btnOne.addEventListener('click', () => {
socket = new WebSocket('ws://127.0.0.1:8090')
socket.addEventListener('open', function () {
console.log('WS 客户端成功连接到服务器')
})
})
// 接受 WS 服务器的消息
let btnTwo = document.getElementById('btnTwo')
btnTwo.addEventListener('click', () => {
socket.addEventListener('message', e => {
console.log(`客户端接收到一个消息: ${e.data}`)
})
})
// WS 客户端想服务器发送消息
let btnThree = document.getElementById('btnThree')
btnThree.addEventListener('click', () => {
socket.send(`你好, 我是客户端: ${new Date().getTime()}`)
})
// 断开与 WS 服务器的连接
let btnFour = document.getElementById('btnFour')
btnFour.addEventListener('click', () => {
socket.close()
})
</script>
</body>