HTML5 Web Worker之性能优化

描述

由于 JavaScript 是单线程的,当执行比较耗时的任务时,就会阻塞主线程并导致页面无法响应,这就是 Web Workers 发挥作用的地方。它允许在一个单独的线程(称为工作线程)中执行耗时的任务。这使得 JavaScript 代码可以在后台执行,而不会阻塞主线程并导致页面无响应。

Web Worker 是一个作为后台线程运行的脚本,具有自己的引擎实例和事件循环。它与主执行线程并行运行,并且不会阻塞事件循环。

主线程(或工作线程本身)可以启动任意数量的工作线程。生成 worker 脚本:

  1. 主线程(或另一个工作线程)向新工作线程发送一条消息,其中包含所有必要的数据。

  2. 工作线程中的事件处理程序执行并开始处理数据。

  3. 完成(或失败)时,工作线程将一条带有计算结果的消息发送回主线程。

  4. 主线程中的事件处理程序执行、解析传入结果并运行必要的操作(例如显示值)。

重要性

Web Workers 为开发人员提供了在 Web 上实现多线程的方式,这对于构建高性能的 Web 应用至关重要。通过将耗时的任务在后台独立于主线程中执行,Web Workers 提高了网页的整体响应性,并使用户体验更加流畅。以下是 Web Workers 在 Web 多线程中的重要性和好处:

通过资源利用率

通过允许耗时任务在后台执行,Web Workers 更有效地利用系统资源,实现更快速和高效的数据处理,并提高整体性能。这对于涉及大量数据处理或图像操作的 Web 应用尤为重要,因为 Web Workers 可以在不影响用户界面的情况下执行这些任务。

增加稳定性和可靠性

通过将耗时任务隔离到单独的 worker 线程中,Web Workers 帮助防止在主线程上执行大量代码时发生崩溃和错误。这使得开发人员更容易编写稳定可靠的 Web 应用,减少用户的烦恼和数据丢失的可能性。

增强安全性

Web Workers 在与主线程分离的隔离环境中运行,这有助于提高 Web 应用的安全性。此隔离防止恶意代码访问或修改主线程或其他 Web Workers 中的数据,降低数据泄露或其他安全漏洞的风险。

更好的资源利用率

Web Workers 可以通过将耗时计算放到后台,使主线程用于处理用户输入和其他任务来帮助提高资源利用率。这有助于提高系统的整体性能并减少崩溃或错误的可能性。此外,通过利用多个 CPU 核心,Web Workers 可以更有效地利用系统资源,实现更快速和高效的数据处理。

Web Workers 还能够实现更好的负载平衡和扩展应用。通过允许任务在多个 worker 线程之间并行执行,Web Workers 可以帮助将工作负荷均匀分配到多个核心或处理器上,实现更快速和高效的数据处理。这对于经历高流量或需求的应用尤为重要,因为 Web Workers 可以帮助确保应用可以处理增加的负载而不影响性能。

Web Workers 客户端使用

使用 JavaScript 创建 Web Worker 的步骤如下:

  1. 创建一个新的 JavaScript 文件,其中包含要在工作线程中运行的代码(耗时任务)。该文件不应包含对 DOM 的引用,因为在工作线程中无法访问 DOM。

  2. 在主 JavaScript 文件中,使用 Worker 构造函数创建一个新的worker对象。此构造函数接收一个参数,即在步骤 1 中创建的 JavaScript 文件的 URL。

const worker = new Worker('worker.js');
  1. worker对象添加事件侦听器以处理主线程和工作线程之间发送的消息。onmessage 用于处理从工作线程发送来的消息,postMessage 用于向工作线程发送消息。

worker.onmessage = function(event) {console.log('Worker: ' + event.data);
};worker.postMessage('Hello, worker!');
  1. 在 Web Worker 的 JavaScript 文件中,使用self对象的onmessage属性添加一个事件监听器来处理从主线程发出的消息。可以使用event.data属性访问发送的消息数据。

self.onmessage = function(event) {console.log('Main: ' + event.data);self.postMessage('Hello, Main!');
};

接下来就运行应用并测试 Worker。可以在控制台看到以下信息,表示主线程和 Worker 线程之间发送和接收了消息。

Main:Hello worker!
Worker:Hello Main!

我们可以使用terminate()函数来终止一个工作线程,或者通过调用self上的close()函数使其自行终止。

// 从应用中终止一个工作线程
worker.terminate();
// 让一个工作线程自行终止
self.close();

可以使用importScripts()函数将库或文件导入到工作线程中,该函数可以接受多个文件。以下示例将script1.jsscript2.js加载到工作线程 worker.js 中:

importScripts('script1.js','script2');

可以使用 onerror函数来处理工作线程抛出的错误:

worker.onerror = function(err) {console.log("遇到错误")
}

Web Workers 服务端应用

服务器端 JavaScript 运行时也支持 Web Worker:

  • Node.js 在版本 10 中实现了类似 Web Worker 的功能,称为 Worker thread(工作进程)。

  • Deno 复制了 Web Worker API,因此语法与浏览器代码完全相同。它还提供了兼容模式,可以填充 Node.js API,以便可以使用该运行时的工作线程语法。

  • Bun 将支持浏览器和 Node.js 的 Web Worker API。

基本使用

要在 Node.js 中使用 Web Worker,主脚本必须定义一个 Worker 对象,其中包含相对于项目根目录的 Web Worker 脚本的名称。第二个参数定义了一个对象,其中包含一个workerData属性,该属性包含要发送的数据:

const worker = new Worker('./worker.js', {workerData: { a: 1, b: 2, c: 3 }
});

与浏览器中的 Web Worker 不同, 它在启动时无需运行worker.postMessage()。如果需要的话,可以调用该方法并稍后发送更多数据,它会触发parentPort.on('message')事件处理程序:

parentPort.on('message', e => {console.log(e);
});

一旦工作线程完成处理,它会使用以下方法将结果数据发送回主线程:

parentPort.postMessage(result);

这将在在主脚本中触发 message 事件,主线程接收到 worker 返回的结果:

worker.on('message', result => {console.log( result );
});

在发送完消息后,worker 就会终止。这也会触发一个exit事件,如果希望运行清理或其他函数,可以利用这个事件:

worker.on('exit', code => {
//...
});

除此之外,还支持其他事件处理:

  • messageerror:当 worker 收到无法反序列化的数据时触发。

  • online:当 worker 开始执行时触发。

  • error:当 worker 脚本中发生 JavaScript 错误时触发。

在服务端,一个单独的 Node.js 脚本文件可以同时包含主线程和工作线程的代码。脚本必须使用isMainThread检查自身是否在主线程上运行,然后将自身作为工作线程进行调用(可以在 ES 模块中使用import.meta.url作为文件引用,或者在 CommonJS 中使用__filename)。

import { Worker, isMainThread, workerData, parentPort } from "node:worker_threads";if (isMainThread) {// 主线程const worker = new Worker(import.meta.url, {workerData: { a: 1, b: 2, c: 3 }});worker.on('message', msg => {});worker.on('exit', code => {});
}
else {// 工作线程const result = runSomeProcess( workerData );parentPort.postMessage(result);
}

这种方式更快,并且对于小型、自包含的单脚本项目来说是一个选择。如果是大型项目,将 worker 脚本文件分开会更容易维护。

数据通信

主线程和工作线程之间的通信涉及到了数据序列化。可以使用表示固定长度原始二进制数据的SharedArrayBuffer对象在线程之间共享数据。以下是一个示例,主线程定义了从 0 到 99 的 100 个数字元素,并将其发送给工作线程:

// main.js
import { Worker } from "node:worker_threads";constbuffer = new SharedArrayBuffer(100 * Int32Array.BYTES_PER_ELEMENT),value = new Int32Array(buffer);value.forEach((v,i) => value[i] = i);const worker = new Worker('./worker.js');worker.postMessage({ value });

工作线程可以接收 value对象:

// worker.js
import { parentPort } from 'node:worker_threads';parentPort.on('message', value => {value[0] = 100;
});

主线程或工作线程都可以更改值数组中的元素,数据将在两个线程之间保持一致。这可能会提高性能,但有一些缺点:

  • 只能共享整数数据。

  • 可能仍需要通知另一个线程更改。

  • 存在两个线程可能同时更新同一值并且失去同步的风险。

Node.js 子进程

在 Node.js 中,除了使用工作线程外,还可以使用子进程来实现类似的功能。子进程用于启动其他应用、传递数据并接收结果。它们与工作线程类似,但通常效率较低,进程开销较大。

子进程和工作线程的选择取决于具体的应用场景。如果只需要在 Node.js 中执行其他任务或命令,子进程是一种更好的选择。但如果需要在 Node.js 中进行复杂的计算或处理任务,Web Worker 可能更适合。

Web Workers 应用场景

Web Workers 在实际应用中有许多常见且有用的应用场景。

处理 CPU 密集任务

假设有一个应用需要执行大量的 CPU 密集型计算。如果在主线程中执行这些计算,用户界面可能会变得无响应,用户体验将受到影响。为了避免这种情况,可以使用 Web Worker 在后台执行这些计算。

在主线程中:

// 创建一个新的 Web Worker
const worker = new Worker('worker.js');// 定义一个函数来处理来自Web Worker的消息
worker.onmessage = function(event) {const result = event.data;console.log(result);
};// 向Web Worker发送一个消息,以启动计算
worker.postMessage({ num: 1000000 });

在 worker.js 中:

// 定义一个函数来执行计算
function compute(num) {let sum = 0;for (let i = 0; i < num; i++) {sum += i;}return sum;
}// 定义一个函数来处理来自主线程的消息
onmessage = function(event) {const num = event.data.num;const result = compute(num);postMessage(result);
};

在这个例子中,创建了一个新的 Web Worker,并定义了一个函数来处理来自 Web Worker 的消息。然后,向 Web Worker 发送一条消息,并提供一个参数(num),指定要执行计算的迭代次数。Web Worker 接收到这条消息后,在后台执行计算。当计算完成后,Web Worker 向主线程发送一条包含结果的消息。主线程收到这个消息后,将结果记录到控制台中。

图片

在上面的例子中,向 Web Worker 的compute()函数传递了数字 1000000。这意味着compute函数将需要将从 0 到一百万的所有数字相加。这涉及大量的额外操作,可能需要很长时间才能完成,特别是如果代码在较慢的计算机上运行或在浏览器标签中同时处理其他任务。

通过将这个任务分配给 Web Worker,应用的主线程可以继续平稳运行,而不会被计算密集型的任务阻塞。这使得用户界面保持响应,并确保其他任务(如用户输入或动画)可以在没有延迟的情况下处理。

处理网络请求

假设有一个应用需要发起大量的网络请求。如果在主线程中执行这些请求,可能会导致用户界面无响应,用户体验差。为了避免这个问题,可以利用 Web Worker 在后台处理这些请求。通过这样做,主线程可以同时执行其他任务,而 Web Worker 负责处理网络请求,从而提高性能和改善用户体验。

在主线程中:

// 创建一个新的 Web Worker
const worker = new Worker('worker.js');// 定义一个函数来处理来自Web Worker的消息
worker.onmessage = function(event) {const response = event.data;console.log(response);
};// 向Web Worker发送一个消息,以启动计算
worker.postMessage({ urls: ['https://api.example.com/foo', 'https://api.example.com/bar'] });

在 worker.js 中:

// 定义一个函数来执行网络请求
function request(url) {return fetch(url).then(response => response.json());
}// 定义一个函数来处理来自主线程的消息
onmessage = async function(event) {const urls = event.data.urls;const results = await Promise.all(urls.map(request));postMessage(results);
};

在这个例子中,创建一个新的 Web Worker 并定义一个函数来处理来自该 Worker 的消息。然后,向 Worker 发送一个包含一组 URL 请求的消息。Worker 接收到这个消息后,在后台使用 fetch API 执行请求。当所有请求完成后,Worker 向主线程发送包含结果的消息。主线程接收到这个消息后,将结果记录到控制台中。

并行处理

假设应用需要执行大量独立计算。 如果在主线程中依次执行这些计算,用户界面将变得无响应,用户体验将受到影响。 为了避免这种情况,可以实例化多个 Web Worker 来并行执行计算。

在主线程中:

// 创建三个新的 Web Worker
const worker1 = new Worker('worker.js');
const worker2 = new Worker('worker.js');
const worker3 = new Worker('worker.js');// 定义三个处理来自 worker 的消息的函数
worker1.onmessage = handleWorkerMessage;
worker2.onmessage = handleWorkerMessage;
worker3.onmessage = handleWorkerMessage;function handleWorkerMessage(event) {const result = event.data;console.log(result);
}// 将任务分配给不同的 worker 对象,并发送消息启动计算
worker1.postMessage({ num: 1000000 });
worker2.postMessage({ num: 2000000 });
worker3.postMessage({ num: 3000000 });

在 worker.js 中:

// 定义一个函数来执行单个计算
function compute(num) {let sum = 0;for (let i = 0; i < num; i++) {sum += i;
}return sum;
}// 定义一个函数来处理来自主线程的消息
onmessage = function(event) {const result = compute(event.data.num);postMessage(result);
};

在这个例子中,创建三个新的 Web Worker 并定义一个函数来处理来自该 Worker 的消息。然后,向三个 Worker 分别发送一个要计算的数字消息。Worker 接收到这个消息后执行计算。当计算完成后,Worker 向主线程发送包含结果的消息。主线程接收到这个消息后,将结果记录到控制台中。

Web Workers 使用限制

Web Worker 是一个提高 Web 应用性能和响应能力的强大工具,但它们也有一些限制和注意事项。

浏览器支持

目前所有主流浏览器、Node.js、Deno 和 Bun 都支持 Web Workers。

图片

 

对 DOM 的访问受到限制

Web Worker 在单独的线程中运行,无法直接访问主线程中的 DOM 或其他全局对象。这意味着不能直接从 Web Worker 中操作 DOM,也不能访问像windowdocument这样的全局对象。

为了解决这个限制,可以使用postMessage方法与主线程进行通信,间接地更新 DOM 或访问全局对象。例如,使用postMessage将数据发送到主线程,然后根据接收到的消息来更新 DOM 或全局对象。

另外,还有一些库可以帮助解决这个问题。例如,WorkerDOM[1] 库允许在 Web Worker 中运行 DOM,从而加快页面的渲染速度并提高性能。

现代桌面浏览器支持共享工作线程,即在不同窗口、iframes 或工作线程中可被多个脚本访问的单个脚本,它们通过独立的端口进行通信。但是,大多数移动浏览器不支持共享工作线程,所以对于大多数 Web 项目来说,它们并不实用。

通信开销大

Web Worker 使用postMessage方法与主线程进行通信,这可能会引入通信开销。通信开销指的是在两个或多个计算系统之间建立和维护通信所需的时间和资源量,比如在 Web 应用中,Web Worker 与主线程之间的通信。这可能导致消息处理延迟,潜在地减慢应用程序的速度。为了最小化这种开销,应该只在线程之间发送必要的数据,避免发送大量数据或频繁发送消息。

调试工具有限

与在主线程中调试代码相比,调试 Web Worker 可能更具挑战性,因为可用的调试工具较少。为了简化调试过程,可以使用控制台 API 在 Worker 线程中记录消息,并使用浏览器开发者工具检查线程之间发送的消息。

代码复杂度

使用 Web Worker 可能会增加代码的复杂性,因为需要管理线程之间的通信,并确保数据正确传递。这可能会使编写、调试和维护代码更加困难,因此应该仔细考虑是否有必要在应用中使用 Web Worker。

Web Workers 实践

上面提到了在使用 Web Workers 时,可能会出现的一些潜在问题。下面就来看看如何缓解这些问题。

worker.on('message', result => {console.log( result );
});

消息批处理

消息批处理涉及将多个消息组合成一个批处理消息,这比单独发送个别消息更有效。这种方法减少了主线程和 Web Worker 之间往返的数量,它有助于最小化通信开销并提高应用的整体性能。

为了实现消息批量处理,可以使用队列来累积消息,并在队列达到一定阈值或经过设定时间后将消息批量发送。下面来在 Web Worker 中简单实现消息的批处理:

// 创建一个消息队列累积消息
const messageQueue = [];// 创建一个将消息添加到队列的函数
function addToQueue(message) {messageQueue.push(message);// 检查队列是否达到阈值大小if (messageQueue.length >= 10) {// 如果是,请将批处理消息发送到主线程postMessage(messageQueue);// 清除消息队列messageQueue.length = 0;}
}// 将消息添加到队列中
addToQueue({type: 'log', message: 'Hello, world!'});// 再添加另一条消息到队列中
addToQueue({type: 'error', message: 'An error occurred.'});

在这个例子中, 创建了一个消息队列,用于累积需要发送到主线程的消息。每当使用addToQueue函数将消息添加到队列时,检查队列是否已达到阈值大小(10)。如果是,则使用postMessage方法将批处理消息发送到主线程。然后,清除消息队列,以准备进行下一次批处理。

通过以这种方式批处理消息,可以减少主线程和 Web Worker 之间发送的消息总数,从而提高应用性能。

避免同步方法

同步方法是阻塞其他代码执行的 JavaScript 函数或操作。同步方法可以阻塞主线程,导致应变得无响应。为了避免这种情况,应尽量避免在 Web Worker 中使用同步方法。而应该使用setTimeout()setInterval()等异步方法来执行长时间运行的计算。

// 在Web Worker中
self.addEventListener('message', (event) => {if (event.data.action === 'start') {// 使用setTimeout来异步执行计算setTimeout(() => {const result = doSomeComputation(event.data.data);// 将结果发送回主线程self.postMessage({ action: 'result', data: result });}, 0);}
});

注意内存使用情况

Web Workers 有自己的内存空间,这个空间根据用户的设备和浏览器设置可能是有限的。为了避免内存问题,应该注意你的 Web Worker 代码使用的内存量,并避免不必要地创建大对象。例如:

// 在Web Worker中
self.addEventListener('message', (event) => {if (event.data.action === 'start') {// 使用for循环处理一个数据数组const data = event.data.data;const result = [];for (let i = 0; i < data.length; i++) {// 处理数组中的每个项,并将结果添加到结果数组中const itemResult = processItem(data[i]);result.push(itemResult);}// 将结果发送回主线程self.postMessage({ action: 'result', data: result });}
});

在这段代码中,Web Worker 处理一个数据数组,并使用postMessage方法将结果发送回主线程。然而,用于处理数据的 for 循环可能耗时较长。

导致这个问题的原因是代码一次性处理了整个数据数组,这意味着所有的数据都必须同时加载到内存中。如果数据集非常大,这可能导致 Web Worker 消耗大量的内存,甚至超过浏览器为 Web Worker 分配的内存限制。

为了缓解这个问题,可以考虑使用内置的 JavaScript 方法,如forEachreduce,它们可以逐项处理数据,避免一次性加载整个数组到内存中。

浏览器兼容性

大多数现代浏览器都支持 Web Worker,但某些较旧的浏览器可能不支持它们。 为了确保与各种浏览器的兼容性,应该在不同的浏览器和版本中测试 Web Worker 代码。 还可以使用功能检测来检查 Web Worker 是否受支持,然后再在代码中使用它们,如下所示:

if (typeof Worker !== 'undefined') {const worker = new Worker('worker.js');
} else {console.log('Web Workers are not supported in this browser.');
}

这段代码会检查当前浏览器是否支持 Web Workers,并在支持时创建一个新的 Web Worker。如果 Web Workers 不受支持,则该代码记录一条消息到控制台,表示该浏览器不支持 Web Workers。

总结

Web Workers 是现代 Web 开发的一个基本特性,它允许开发人员将 CPU 密集型任务放到单独的线程中执行,从而提高应用的性能和响应能力。然而,在处理 Web Workers 时需要记住一些重要的限制和注意事项,例如无法访问 DOM 和数据类型之间传递的限制等。为了避免这些潜在问题,可以采用上面提到的策略,如使用异步方法并注意卸载的任务的复杂性。在未来,使用 Web Workers 进行多线程似乎仍然是提高 Web 应用程序性能和响应能力的重要技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/734834.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第三周组会——动态多目标优化算法

首先对上周写的DF测试函数进行了优化和增加 DF4 pf: DF5测试函数PF DF6 遇到的问题,在算法问题的参数taut(变化频率)默认是10数字变小时就算是9,算法会跟不上收敛 新读的文献 A Novel Dynamic Multiobjective Optimization Algorithm With Hierarchical Response System 一…

1.2_3 TCP/IP参考模型

文章目录 1.2_3 TCP/IP参考模型&#xff08;一&#xff09;OSI参考模型与TCP/IP参考模型&#xff08;二&#xff09;5层参考模型&#xff08;三&#xff09;5层参考模型的数据封装与解封装 1.2_3 TCP/IP参考模型 &#xff08;一&#xff09;OSI参考模型与TCP/IP参考模型 TCP/I…

【理解指针(1)】

理解指针&#xff08;1&#xff09; 1什么是内存2指针变量和地址21 取地址操作符&#xff08;&&#xff09;22 指针变量23 解引用操作符&#xff08;*&#xff09;24 指针变量的大小 3指针变量的意义31指针的解引用32 指针加减整数33 void* 指针 4. const 修饰指针41 const…

递归搜索回溯相关的题目解析和练习2

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;下面的题目用的方法和前面几篇的方法是一样的&#xff0c;写起来是比较困难的&#xff0c;加油 字母大小写全排列 https://leetcode.cn/problems/letter-case-permutation/ 解析 代码 class Solution {vector<string&g…

【Docker】容器的概念

容器技术&#xff1a;容器技术是基于虚拟化技术的&#xff0c;它使应用程序从一个计算机环境快速可靠地转移到另一个计算机环境中&#xff0c;可以说是一个新型地虚拟化技术。 一、docker容器 Docker:是一个开源地容器引擎Docker 是一种轻量级的容器化技术&#xff0c;其主要原…

分割模型TransNetR的pytorch代码学习笔记

这个模型在U-net的基础上融合了Transformer模块和残差网络的原理。 论文地址&#xff1a;https://arxiv.org/pdf/2303.07428.pdf 具体的网络结构如下&#xff1a; 网络的原理还是比较简单的&#xff0c; 编码分支用的是预训练的resnet模块&#xff0c;解码分支则重新设计了。…

PyTorch搭建LeNet训练集详细实现

一、下载训练集 导包 import torch import torchvision import torch.nn as nn from model import LeNet import torch.optim as optim import torchvision.transforms as transforms import matplotlib.pyplot as plt import numpy as npToTensor()函数&#xff1a; 把图像…

git学习(创建项目提交代码)

操作步骤如下 git init //初始化git remote add origin https://gitee.com/aydvvs.git //建立连接git remote -v //查看git add . //添加到暂存区git push 返送到暂存区git status // 查看提交代码git commit -m初次提交git push -u origin "master"//提交远程分支 …

微信小程序(五十二)开屏页面效果

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.使用控件模拟开屏界面 2.倒计时逻辑 3.布局方法 4.TabBar隐藏复现 源码&#xff1a; components/openPage/openPage.wxml <view class"openPage-box"><image src"{{imagePath}}"…

三维不同坐标系下点位姿态旋转平移变换

文章目录 前言正文计算方法思路Python实现总结前言 本文主要说明以下几种场景3D变换的应用: 3D相机坐标系下长方体物体,有本身坐标系,沿该物体长边方向移动一段距离,并绕长边轴正旋转方向转90度,求解当前物体中心点在相机坐标系下的位置和姿态多关节机器人末端沿工具坐标…

STM32 利用FlashDB库实现在线扇区数据管理不丢失

STM32 利用FlashDB库实现在线扇区数据管理不丢失 &#x1f4cd;FalshDB地址:https://gitee.com/Armink/FlashDB ✨STM32没有片内EEPROM这样的存储区&#xff0c;虽然有备份寄存器&#xff0c;仅可以实现对少量数据的频繁存储&#xff0c;但是依赖备份电源&#xff08;BAT引脚&a…

美国签证|附面签相关事项√

小伙伴最近都忙着办签证吧&#xff01;但是需要注意的是&#xff0c;美国的签证跟其他任何国家的签证不同&#xff0c;并不是办理了就一定拿得到&#xff0c;据说概率是50%左右。所以办理美国签证&#xff0c;不要太着急啦&#xff01;先来了解一下美国签证的相片该怎么拍叭 ✅…

RocketMQ的事务消息流程

什么是事务消息&#xff1f; 事务消息是一种在发送方和接收方之间保证消息传递的一致性和可靠性的消息传递机制。在消息发送过程中&#xff0c;生产者可以将消息发送到消息队列&#xff0c;但不会立即被消费者接收和处理。相反&#xff0c;消息会先进入一种“准备”状态&#x…

用chatgpt写insar地质灾害的论文,重复率只有1.8%,chatgpt4.0写论文不是梦

突发奇想&#xff0c;想用chatgpt写一篇论文&#xff0c;并看看查重率&#xff0c;结果很惊艳&#xff0c;说明是确实可行的&#xff0c;请看下图。 下面是完整的文字内容。 InSAR (Interferometric Synthetic Aperture Radar) 地质灾害监测技术是一种基于合成孔径雷达…

【JavaScript】JavaScript 变量 ① ( JavaScript 变量概念 | 变量声明 | 变量类型 | 变量初始化 | ES6 简介 )

文章目录 一、JavaScript 变量1、变量概念2、变量声明3、ES6 简介4、变量类型5、变量初始化 二、JavaScript 变量示例1、代码示例2、展示效果 一、JavaScript 变量 1、变量概念 JavaScript 变量 是用于 存储数据 的 容器 , 通过 变量名称 , 可以 获取 / 修改 变量 中的数据 ; …

第十五届蓝桥杯模拟赛(第三期)

大家好&#xff0c;我是晴天学长&#xff0c;本次分享&#xff0c;制作不易&#xff0c;本次题解只用于学习用途&#xff0c;如果有考试需要的小伙伴请考完试再来看题解进行学习&#xff0c;需要的小伙伴可以点赞关注评论一波哦&#xff01;蓝桥杯省赛就要开始了&#xff0c;祝…

【DimPlot】【FeaturePlot】使用小tips

目录 DimPlot函数参数解析 栅格化点图 放大 ggplot2 图例的点&#xff0c;修改图例的标题 FeaturePlot函数参数解析 调整FeaturePlot颜色 分组绘制featureplot 随手笔记&#xff0c;持续更新中。。。 Reference DimPlot函数参数解析 object: 一个Seurat对象&#xff0c;…

工作纪实46-关于微服务的上线发布姿势

蓝绿部署 在部署时&#xff0c;不需要将旧版本的服务停掉&#xff0c;而是将新版本与旧版本同时运行&#xff0c;新版本测试无误之后再将旧版本停掉。这样可以避免再升级的过程中如果失败服务不可用的问题&#xff0c;因为同时部署了两个版本的程序&#xff0c;使得硬件资源是…

【项目笔记】java微服务:黑马头条(day01)

文章目录 环境搭建、SpringCloud微服务(注册发现、服务调用、网关)1)课程对比2)项目概述2.1)能让你收获什么2.2)项目课程大纲2.3)项目概述2.4)项目术语2.5)业务说明 3)技术栈4)nacos环境搭建4.1)虚拟机镜像准备4.2)nacos安装 5)初始工程搭建5.1)环境准备5.2)主体结构 6)登录6.1…

JavaScript中的Set和Map:理解与使用

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…