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.rhkb.cn/news/273241.html

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

相关文章

vue iis 配置

下载安装两个IIS模块 1). 传送门&#xff1a;URL Rewrite 2). 传送门&#xff1a;Application Request Routing 注 : 只有在 服务器的主页 有Application Request Routing 部署VUE网站 生成网站 在VUE项目打包生成出发布文件,即文件夹 dist,此处忽略 复制到你需要存放网站的…

Vue 监听器:让你的应用实时响应变化

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

加快代码审查的 7 个最佳实践

目录 前言 1-保持小的拉取请求 2-使用拉取请求模板 3-实施响应时间 SLA 4-培训初级和中级工程师 5-设置持续集成管道 6-使用拉取请求审查应用程序 7-生成图表以可视化您的代码更改 前言 代码审查可能会很痛苦软件工程师经常抱怨审查过程缓慢&#xff0c;延迟下游任务&…

Linux练习题

1、查看后台进程作业ID的指令是(A) A. jobs B. ps C. ls D. pg 2、在Linux系统的vi编辑器中&#xff0c;如果不保存对文件进行的修改&#xff0c;应使用(D )命令强制退出vi编辑器 A. :q B. :wq C. :q! D: :!q 3、列出当前目录下以…

Python实现ETS指标平滑模型(ETSModel算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 ETS模型&#xff08;Error-Trend-Seasonality Model&#xff09;&#xff0c;是一种广泛应用于时间序列…

QT----云服务器部署Mysql,Navicat连接1698 -Access denied for user ‘root‘@‘‘

阿里云有活动&#xff0c;白嫖了一年的新加坡轻量级服务器&#xff0c;有点卡&#xff0c;有时候要开梯子 白嫖300元优惠券 目录 1 安装启动Mysql服务2 更改连接权限2.1 Navicat连接报错1698 -Access denied for user root 3 qt连接云服务器数据库 1 安装启动Mysql服务 我使用…

算法第二十六天-删除有序数组中的重复项Ⅱ

删除有序数组中的重复项 题目要求 解题思路 题目要求中提到原地修改&#xff0c;那么肯定需要一个指针指向当前即将放置元素的位置&#xff0c;需要另外一个指针向后遍历所有元素&#xff0c;所以[双指针]解法呼之欲出。 慢指针slow&#xff1a;指向当前元素放置的位置&…

深入了解二叉搜索树:原理、实现与应用

目录 一、介绍二叉搜索树 二、二叉搜索树的基本性质 三、二叉搜索树的实现 四、总结 在计算机科学中&#xff0c;数据结构是构建算法和程序的基础。其中&#xff0c;二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称 BST&#xff09;作为一种常见的数据结构&#…

JavaEE+springboot教学仪器设备管理系统o9b00-springmvc

本文旨在设计一款基于Java技术的教学仪器设备销售网站&#xff0c;以提高网站性能、功能完善、用户体验等方面的优势&#xff0c;解决现有教学仪器设备销售网站的问题&#xff0c;并为广大教育工作者和学生提供便捷的教学仪器设备销售渠道。本文首先介绍了Java技术的相关基础知…

CSS拖曳盒子案例

让我为大家带来一个小案例吧&#xff01; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>* {margin: 0;padding: 0;}.box1 {width: 100px;height: 100px;background-color: black;margin-bot…

大载重无人机基础技术,研发一款50KG负重六旋翼无人机技术及成本分析

六旋翼无人机是一种多旋翼无人机&#xff0c;具有六个旋翼&#xff0c;通常呈“X”形布局。它采用电动串列式结构&#xff0c;具有垂直起降、悬停、前飞、后飞、侧飞、俯仰、翻滚等多种飞行动作的能力。六旋翼无人机通常被用于航拍、农业植保、环境监测、地形测绘等领域。 六旋…

Django工具

一、分页器介绍 1.1、介绍 分页,就是当我们在页面中显示一些信息列表,内容过多,一个页面显示不完,需要分成多个页面进行显示时,使用的技术就是分页技术 在django项目中,一般是使用3种分页的技术: 自定义分页功能,所有的分页功能都是自己实现django的插件 django-pagin…

数据库(mysql)-新手笔记(主外键,视图)

数据库基本知识点- http://t.csdnimg.cn/CVa9e 主外键 主键(唯一性,非空性) 主键是数据库表中的一个或多个字段&#xff0c;其值唯一标识表中的每一行/记录。 唯一性: 主键字段中的每个值都必须是唯一的&#xff0c;不能有两个或更多的记录具有相同的主键值 非空性&#x…

稀碎从零算法笔记Day14-LeetCode:同构字符串

题型&#xff1a;字符串、哈希表 链接&#xff1a;205. 同构字符串 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那…

红队攻防之Go上线基础免杀(一)

不堪风雨乱红尘&#xff0c;情到真时恰是空 加载bypass插件 使用插件生成shellcode.txt文件 选择监听器和配置 使用插件生成的shellcode文件如下&#xff1a; process_xxx xxx,...... > code.txtprocess_xxx xxx > code1.txtprocess_xxx xxx > code2.txt将生成的三个…

SPSS26安装后无法启动,提示:应用程序的并行配置不正确

以下的解决方法供参考&#xff1a; 1、安装jdk并配置 2、 找到安装目录\Statistics\26\VC9下的vcredist_x64.exe&#xff0c;打开安装并选择“repair”&#xff0c;安装完成后重启&#xff0c;一般可以成功。 3、若还不行&#xff0c;安装较新的C运行库&#xff0c;再试试。 …

深入理解 Webpack 热更新原理:提升开发效率的关键

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

(001)UV 的使用以及导出

文章目录 UV窗口导出模型的主要事项导出时材质的兼容问题unity贴图导出导出FBX附录 UV窗口 1.uv主要的工作区域&#xff1a; 2.在做 uv 和贴图之前&#xff0c;最好先应用下物体的缩放、旋转。 导出模型的主要事项 1.将原点设置到物体模型的底部&#xff1a; 2.应用修改器的…

部署LVS+Keepalived高可用群集(抢占模式,非抢占模式,延迟模式)

目录 一、LVSKeepalived高可用群集 1、实验环境 2、 主和备keepalived的配置 2.1 yum安装ipvsadm和keepalived工具 2.2 添加ip_vs模块并开启ipvsadm 2.3 修改keepalived的配置文件 2.4 调整proc响应参数&#xff0c;关闭linux内核的重定向参数响应 2.5 将主服务器的kee…

华为OD机试C卷“跳步-数组”Java解答

描述 示例 算法思路1 不断移动数组将元素删去&#xff08;并未彻底删除&#xff0c;而是将数字元素前移实现“伪删除”&#xff09;这样删除元素的位置就呈现一定规律&#xff0c;详细见下图&#xff08;潦草的画&#xff09; 答案1 import java.util.*;public class Main {…