当前位置: 首页 > news >正文

HarmonyOS:多线程并发-Worker

Worker主要作用是为应用程序提供一个多线程的运行环境,可满足应用程序在执行过程中与宿主线程分离,在后台线程中运行一个脚本进行耗时操作,极大避免类似于计算密集型或高延迟的任务阻塞宿主线程的运行。具体接口信息及使用方法详情请见Worker。

一、Worker运作机制

Worker运作机制示意图
在这里插入图片描述

创建Worker的线程称为宿主线程(不一定是主线程,工作线程也支持创建Worker子线程),Worker自身的线程称为Worker子线程(或Actor线程、工作线程)。每个Worker子线程与宿主线程拥有独立的实例,包含基础设施、对象、代码段等,因此每个Worker启动存在一定的内存开销,需要限制Worker的子线程数量。Worker子线程和宿主线程之间的通信是基于消息传递的,Worker通过序列化机制与宿主线程之间相互通信,完成命令及数据交互。

二、Worker注意事项

  • 创建Worker时,有手动和自动两种创建方式,手动创建Worker线程目录及文件时,还需同步进行相关配置,详情请参考创建Worker的注意事项。
  • 使用Worker能力时,构造函数中传入的Worker线程文件的路径在不同版本有不同的规则,详情请参见文件路径注意事项
  • Worker创建后需要手动管理生命周期,且最多同时运行的Worker子线程数量为64个,详情请参见生命周期注意事项
  • 由于不同线程中上下文对象是不同的,因此Worker线程只能使用线程安全的库,例如UI相关的非线程安全库不能使用。
  • 序列化传输的数据量大小限制为16MB。
  • 使用Worker模块时,需要在宿主线程中注册onerror接口,否则当Worker线程出现异常时会发生jscrash问题。
  • 不支持跨HAP使用Worker线程文件。
  • 引用HAR/HSP前,需要先配置对HAR/HSP的依赖,详见引用共享包
  • 不支持在Worker工作线程中使用AppStorage。

三、创建Worker的注意事项

Worker线程文件需要放在"{moduleName}/src/main/ets/"目录层级之下,否则不会被打包到应用中。有手动和自动两种创建Worker线程目录及文件的方式。

  • 手动创建:开发者手动创建相关目录及文件,此时需要配置build-profile.json5的相关字段信息,Worker线程文件才能确保被打包到应用中。
    Stage模型:
"buildOption": {"sourceOption": {"workers": ["./src/main/ets/workers/worker.ets"]}
}

FA模型:

"buildOption": {"sourceOption": {"workers": ["./src/main/ets/MainAbility/workers/worker.ets"]}
}
  • 自动创建:DevEco Studio支持一键生成Worker,在对应的{moduleName}目录下任意位置,点击鼠标右键 > New > Worker,即可自动生成Worker的模板文件及配置信息,无需再手动在build-profile.json5中进行相关配置。

    在这里插入图片描述

四、文件路径注意事项

当使用Worker模块具体功能时,均需先构造Worker实例对象,其构造函数与API版本相关,且构造函数需要传入Worker线程文件的路径(scriptURL)。

// 导入模块
import { worker } from '@kit.ArkTS';// API 9及之后版本使用:
const worker1: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');
// API 8及之前版本使用:
const worker2: worker.Worker = new worker.Worker('entry/ets/workers/worker.ets');
4.1 Stage模型下的文件路径规则

构造函数中的scriptURL要求如下:

  • scriptURL的组成包含 {moduleName}/ets 和相对路径 relativePath。
  • relativePath是Worker线程文件相对于"{moduleName}/src/main/ets/"目录的相对路径。
4.1.1 加载Ability中Worker线程文件场景

加载Ability中的worker线程文件,加载路径规则:{moduleName}/ets/{relativePath}。

import { worker } from '@kit.ArkTS';// worker线程文件所在路径:"entry/src/main/ets/workers/worker.ets"
const workerStage1: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');// worker线程文件所在路径:"phone/src/main/ets/ThreadFile/workers/worker.ets"
const workerStage2: worker.ThreadWorker = new worker.ThreadWorker('phone/ets/ThreadFile/workers/worker.ets');
4.1.2 加载HSP中Worker线程文件场景

加载HSP中worker线程文件,加载路径规则:{moduleName}/ets/{relativePath}。

import { worker } from '@kit.ArkTS';// worker线程文件所在路径: "hsp/src/main/ets/workers/worker.ets"
const workerStage3: worker.ThreadWorker = new worker.ThreadWorker('hsp/ets/workers/worker.ets');
4.1.3 加载HAR中Worker线程文件场景

加载HAR中worker线程文件存在以下两种情况:

  • @标识路径加载形式:所有种类的模块加载本地HAR中的Worker线程文件,加载路径规则:@{moduleName}/ets/{relativePath}。
  • 相对路径加载形式:本地HAR加载该包内的Worker线程文件,加载路径规则:创建Worker对象所在文件与Worker线程文件的相对路径。

说明
当开启useNormalizedOHMUrl(即将工程目录中与entry同级别的应用级build-profile.json5文件中strictMode属性的useNormalizedOHMUrl字段配置为true)或HAR包会被打包成三方包使用时,则HAR包中使用Worker仅支持通过相对路径的加载形式创建。

import { worker } from '@kit.ArkTS';// @标识路径加载形式:
// worker线程文件所在路径: "har/src/main/ets/workers/worker.ets"
const workerStage4: worker.ThreadWorker = new worker.ThreadWorker('@har/ets/workers/worker.ets');// 相对路径加载形式:
// worker线程文件所在路径: "har/src/main/ets/workers/worker.ets"
// 创建Worker对象的文件所在路径:"har/src/main/ets/components/mainpage/MainPage.ets"
const workerStage5: worker.ThreadWorker = new worker.ThreadWorker('../../workers/worker.ets');
4.2 FA模型下的文件路径规则

构造函数中的scriptURL为:Worker线程文件与"{moduleName}/src/main/ets/MainAbility"的相对路径。

import { worker } from '@kit.ArkTS';// 主要说明以下三种场景:// 场景1: Worker线程文件所在路径:"{moduleName}/src/main/ets/MainAbility/workers/worker.ets"
const workerFA1: worker.ThreadWorker = new worker.ThreadWorker("workers/worker.ets", {name:"first worker in FA model"});// 场景2: Worker线程文件所在路径:"{moduleName}/src/main/ets/workers/worker.ets"
const workerFA2: worker.ThreadWorker = new worker.ThreadWorker("../workers/worker.ets");// 场景3: Worker线程文件所在路径:"{moduleName}/src/main/ets/MainAbility/ThreadFile/workers/worker.ets"
const workerFA3: worker.ThreadWorker = new worker.ThreadWorker("ThreadFile/workers/worker.ets");

五、生命周期注意事项

  • Worker的创建和销毁耗费性能,建议开发者合理管理已创建的Worker并重复使用。Worker空闲时也会一直运行,因此当不需要Worker时,可以调用terminate()接口或close()方法主动销毁Worker。若Worker处于已销毁或正在销毁等非运行状态时,调用其功能接口,会抛出相应的错误。
  • Worker的数量由内存管理策略决定,设定的内存阈值为1.5GB和设备物理内存的60%中的较小者。在内存允许的情况下,系统最多可以同时运行64个Worker。如果尝试创建的Worker数量超出这一上限,系统将抛出错误:“Worker initialization failure, the number of workers exceeds the maximum.”。实际运行的Worker数量会根据当前内存使用情况动态调整。一旦所有Worker和主线程的累积内存占用超过了设定的阈值,系统将触发内存溢出(OOM)错误,导致应用程序崩溃。

六、跨har包加载Worker

  1. 创建har详情参考开发静态共享包。
  2. 在har中创建Worker线程文件相关内容。
// worker.ets
workerPort.onmessage = (e: MessageEvents) => {console.info("worker thread receive message: ", e.data);workerPort.postMessage('worker thread post message to main thread');
}
  1. 在entry模块的oh-package.json5文件中配置har包的依赖。
// 在entry模块配置har包的依赖
{"name": "entry","version": "1.0.0","description": "Please describe the basic information.","main": "","author": "","license": "","dependencies": {"har": "file:../har"}
}
  1. 在entry模块中加载har包中的Worker线程文件。
// Index.ets
import { worker } from '@kit.ArkTS';@Entry
@Component
struct Index {@State message: string = 'Hello World';build() {RelativeContainer() {Text(this.message).id('HelloWorld').fontSize(50).fontWeight(FontWeight.Bold).alignRules({center: { anchor: '__container__', align: VerticalAlign.Center },middle: { anchor: '__container__', align: HorizontalAlign.Center }}).onClick(() => {// 通过@标识路径加载形式,加载har中Worker线程文件let workerInstance = new worker.ThreadWorker('@har/ets/workers/worker.ets');workerInstance.onmessage = () => {console.info('main thread onmessage');};workerInstance.postMessage('hello world');})}.height('100%').width('100%')}
}

七、多级Worker生命周期管理

由于支持创建多级Worker(即通过父Worker创建子Worker的机制形成层级线程关系),且Worker线程生命周期由用户自行管理,因此需要注意多级Worker生命周期的正确管理。若用户销毁父Worker时未能结束其子Worker的运行,会产生不可预期的结果。建议用户确保子Worker的生命周期始终在父Worker生命周期范围内,并在销毁父Worker前先销毁所有子Worker。

7.1 推荐使用示例
// 在主线程中创建Worker线程(父Worker),在worker线程中再次创建Worker线程(子Worker)
// main thread
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';// 主线程中创建父worker对象
const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");parentworker.onmessage = (e: MessageEvents) => {console.info("主线程收到父worker线程信息 " + e.data);
}parentworker.onexit = () => {console.info("父worker退出");
}parentworker.onerror = (err: ErrorEvent) => {console.info("主线程接收到父worker报错 " + err);
}parentworker.postMessage("主线程发送消息给父worker-推荐示例");
// parentworker.ets
import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';// 创建父Worker线程中与主线程通信的对象
const workerPort: ThreadWorkerGlobalScope = worker.workerPort;workerPort.onmessage = (e : MessageEvents) => {if (e.data == "主线程发送消息给父worker-推荐示例") {let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");childworker.onmessage = (e: MessageEvents) => {console.info("父Worker收到子Worker的信息 " + e.data);if (e.data == "子Worker向父Worker发送信息") {workerPort.postMessage("父Worker向主线程发送信息");}}childworker.onexit = () => {console.info("子Worker退出");// 子Worker退出后再销毁父WorkerworkerPort.close();}childworker.onerror = (err: ErrorEvent) => {console.info("子Worker发生报错 " + err);}childworker.postMessage("父Worker向子Worker发送信息-推荐示例");}
}
// childworker.ets
import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';// 创建子Worker线程中与父Worker线程通信的对象
const workerPort: ThreadWorkerGlobalScope = worker.workerPort;workerPort.onmessage = (e: MessageEvents) => {if (e.data == "父Worker向子Worker发送信息-推荐示例") {// 子Worker线程业务逻辑...console.info("业务执行结束,然后子Worker销毁");workerPort.close();}
}

执行结果

在这里插入图片描述

7.2 不推荐使用示例

不建议父Worker主动销毁后,子Worker仍向父Worker发送消息。

// main thread
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");parentworker.onmessage = (e: MessageEvents) => {console.info("主线程收到父Worker信息" + e.data);
}parentworker.onexit = () => {console.info("父Worker退出");
}parentworker.onerror = (err: ErrorEvent) => {console.info("主线程接收到父Worker报错 " + err);
}parentworker.postMessage("主线程发送消息给父Worker");
// parentworker.ets
import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';const workerPort: ThreadWorkerGlobalScope = worker.workerPort;workerPort.onmessage = (e : MessageEvents) => {console.info("父Worker收到主线程的信息 " + e.data);let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets")childworker.onmessage = (e: MessageEvents) => {console.info("父Worker收到子Worker的信息 " + e.data);}childworker.onexit = () => {console.info("子Worker退出");workerPort.postMessage("父Worker向主线程发送信息");}childworker.onerror = (err: ErrorEvent) => {console.info("子Worker发生报错 " + err);}childworker.postMessage("父Worker向子Worker发送信息");// 创建子Worker后,销毁父WorkerworkerPort.close();
}

不建议在明确父Worker发起销毁操作的同步调用前后仍在父Worker线程创建子Worker。不建议在不确定父Worker是否发起销毁操作的情况下,仍在父Worker线程创建子Worker,即创建子Worker线程成功之前需保证父Worker线程始终处于存活状态。

// main thread
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");parentworker.onmessage = (e: MessageEvents) => {console.info("主线程收到父Worker信息" + e.data);
}parentworker.onexit = () => {console.info("父Worker退出");
}parentworker.onerror = (err: ErrorEvent) => {console.info("主线程接收到父Worker报错 " + err);
}parentworker.postMessage("主线程发送消息给父Worker");
// parentworker.ets
import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';const workerPort: ThreadWorkerGlobalScope = worker.workerPort;workerPort.onmessage = (e : MessageEvents) => {console.info("父Worker收到主线程的信息 " + e.data);// 父Worker销毁后创建子Worker,行为不可预期workerPort.close();let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");// 子Worker线程未确认创建成功前销毁父Worker,行为不可预期// let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");// workerPort.close();childworker.onmessage = (e: MessageEvents) => {console.info("父Worker收到子Worker的信息 " + e.data);}childworker.onexit = () => {console.info("子Worker退出");workerPort.postMessage("父Worker向主线程发送信息");}childworker.onerror = (err: ErrorEvent) => {console.info("子Worker发生报错 " + err);}childworker.postMessage("父Worker向子Worker发送信息");
}
// childworker.ets
import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';const workerPort: ThreadWorkerGlobalScope = worker.workerPort;workerPort.onmessage = (e: MessageEvents) => {console.info("子Worker收到信息 " + e.data);
}

八、ThreadWorker

使用以下方法前,均需先构造ThreadWorker实例,ThreadWorker类继承WorkerEventTarget。

8.1 constructor

constructor(scriptURL: string, options?: WorkerOptions)

ThreadWorker构造函数。

  • 元服务API:从API version 11 开始,该接口支持在元服务中使用。
  • 系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
script URLstringWorker线程文件的路径。路径规则详细参考文件路径注意事项。
optionsWorkerOptionsWorker构造的选项。

示例:

此处以在Stage模型中Ability加载Worker文件为例,使用Library加载Worker线程文件的场景参考文件路径注意事项。

import { worker } from '@kit.ArkTS';// 主要说明以下两种场景:// 场景1: worker文件所在路径:"entry/src/main/ets/workers/worker.ets"
const workerStageModel01 = new worker.ThreadWorker('entry/ets/workers/worker.ets', {name:"first worker in Stage model"});// 场景2: worker文件所在路径:"phone/src/main/ets/ThreadFile/workers/worker.ets"
const workerStageModel02 = new worker.ThreadWorker('phone/ets/ThreadFile/workers/worker.ets');

九、postMessage

postMessage(message: Object, transfer: ArrayBuffer[]): void

宿主线程通过转移对象所有权的方式向Worker线程发送消息。

系统能力: SystemCapability.Utils.Lang

元服务API:从API version 11 开始,该接口支持在元服务中使用。

参数:

参数名类型必填说明
messageObject发送至Worker的数据,该数据对象必须是可序列化,序列化支持类型见其他说明。
transferArrayBuffer[]表示可转移的ArrayBuffer实例对象数组,该数组中对象的所有权会被转移到Worker线程,在宿主线程中将会变为不可用,仅在Worker线程中可用,数组不可传入null。

示例:

Worker.ets代码

// Worker.ets
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';// 创建worker线程中与宿主线程通信的对象
const workerPort = worker.workerPort// worker线程接收宿主线程信息
workerPort.onmessage = (e: MessageEvents): void => {// data:宿主线程发送的信息let data: number = e.data;// 往收到的buffer里写入数据const view = new Int8Array(data).fill(3);// worker线程向宿主线程发送信息workerPort.postMessage(view);
}// worker线程发生error的回调
workerPort.onerror = (err: ErrorEvent) => {console.log("worker.ets onerror" + err.message);
}

Index.ets代码

// Index.ets
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';@Entry
@Component
struct Index {@State message: string = 'Hello World';build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {// 宿主线程中创建Worker对象const workerInstance = new worker.ThreadWorker("entry/ets/workers/Worker.ets");// 宿主线程向worker线程传递信息const buffer = new ArrayBuffer(8);workerInstance.postMessage(buffer, [buffer]);// 宿主线程接收worker线程信息workerInstance.onmessage = (e: MessageEvents): void => {// data:worker线程发送的信息let data: number = e.data;console.info("main thread data is  " + data);// 销毁Worker对象workerInstance.terminate();}// 在调用terminate后,执行onexitworkerInstance.onexit = (code) => {console.log("main thread terminate");}workerInstance.onerror = (err: ErrorEvent) => {console.log("main error message " + err.message);}})}.width('100%').height('100%')}}
}

执行结果

在这里插入图片描述

相关文章:

HarmonyOS:多线程并发-Worker

Worker主要作用是为应用程序提供一个多线程的运行环境,可满足应用程序在执行过程中与宿主线程分离,在后台线程中运行一个脚本进行耗时操作,极大避免类似于计算密集型或高延迟的任务阻塞宿主线程的运行。具体接口信息及使用方法详情请见Worker…...

小程序IOS安全区域优化:safe-area-inset-bottom

ios下边有一个小黑线,位于底部的元素会被黑线阻挡 safe-area-inset-bottom 一 用法及作用: IOS全面屏底部有小黑线,位于底部的元素会被黑线阻挡,可以使用以下样式: .model{padding-bottom: constant(safe-area-ins…...

C++ 中多态性在实际项目中的应用场景

C中的多态性是面向对象编程中的一个核心概念,它允许我们在使用基类指针或引用的情况下,调用派生类对象的特定方法。这种特性在实际项目中有着广泛的应用场景,具体包括但不限于以下几个方面: 1.图形图像处理: 在图形图…...

prettier配置

配置 Prettier 在 VSCode 中自动格式化代码的教程 1. 安装 Prettier VSCode 插件 打开 VSCode。点击左侧活动栏的扩展市场图标(或按 Ctrl+Shift+X)。在搜索栏中输入 Prettier - Code formatter。找到插件并点击 Install 安装它。2. 配置 VSCode 设置 确保 VSCode 配置正确,…...

【基于OpenEuler国产操作系统大数据实验环境搭建】

大数据实验环境搭建 一、实验简介1.1 实验内容1.2 环境及其资源规划 二、实验目的三、实验过程3.1 安装虚拟机软件及操作系统3.2 创建安装目录(在主节点上操作)3.2 安装JDK及基本设置(所有节点都需要操作)3.3 安装Hadoop3.4 安装Z…...

期末软件经济学

文章目录 前言复习策略复习名词解释简答题第一章 ppt后记 前言 最近白天都在忙正事,晚上锻炼一下,然后处理一些杂事,现在是晚上十点多,还有一些时间复习一下期末考试。复习到十一点。 复习策略 感觉比较简单,直接刷…...

滑动窗口算法专题

滑动窗口简介 滑动窗口就是利用单调性,配合同向双指针来优化暴力枚举的一种算法。 该算法主要有四个步骤 1. 先进进窗口 2. 判断条件,后续根据条件来判断是出窗口还是进窗口 3. 出窗口 4.更新结果,更新结果这个步骤是不确定的&#xff0c…...

基于Java的世界时区自动计算及时间生成方法

目录 前言 一、zoneinfo简介 1、zoneinfo是什么 2、zoneinfo有什么 二、在Java中进行时区转换 1、Java与zoneInfo 2、Java展示zoneInfo实例 3、Java获取时区ID 三、Java通过经纬度获取时区 1、通过经度求解偏移 2、通过偏移量计算时间 3、统一的处理算法 四、总结 …...

Excel + Notepad + CMD 命令行批量修改文件名

注意:该方式为直接修改原文件的文件名,不会生成新文件 新建Excel文件 A列:固定为 renB列:原文件名称C列:修改后保存的名称B列、C列,需要带文件后缀,为txt文件就是.txt结尾,为png图片…...

OpenGL 几何着色器高级应用

几何着色器高级应用 概念回顾 几何着色器(Geometry Shader)是 OpenGL 管线中的可选着色器阶段,位于顶点着色器(Vertex Shader) 和光栅化阶段 之间。 其核心功能是基于输入的图元(如点、线或三角形),生成新的图元,或对输入的图元进行修改。 几何着色器的执行是以图元…...

【Unity基础】Unity 2D实现拖拽功能的10种方法

方法1. 基于 Update 循环的拖拽方法 (DragDrop2D) 代码概述 using System.Collections; using System.Collections.Generic; using UnityEngine;public class DragDrop2D : MonoBehaviour {bool isDraggable;bool isDragging;Collider2D objectCollider;void Start(){objectC…...

duxapp中兼容多端的 BoxShadow 阴影组件

由于RN 安卓端对阴影的支持不太完善&#xff0c;使用这个组件可以实现阴影效果 在RN端是使用 react-native-fast-shadow 实现的 示例 import { BoxShadow, Text } from /duxui<BoxShadow><Text>这是内容</Text> </BoxShadow>Props 继承自Taro的View…...

服务器---centos上安装docker并使用docker配置jenkins

要在 Docker 中安装 Jenkins 并进行管理,可以按照以下步骤操作: 1. 安装 Docker 首先,确保你的系统已经安装了 Docker。如果尚未安装,可以使用以下命令进行安装: 在 CentOS 上安装 Docker sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://…...

Linux系统操作03|chmod、vim

上文&#xff1a; Linux系统操作02|基本命令-CSDN博客 目录 六、chmod&#xff1a;给文件设置权限 1、字母法 2、数字法&#xff08;用的最多&#xff09; 七、vim&#xff1a;代码编写和文本编辑 1、启动和退出 1️⃣启动 2️⃣退出 2、vim基本操作 六、chmod&#x…...

数据库同步中间件DBSyncer安装配置及使用

1、介绍 DBSyncer&#xff08;英[dbsɪŋkɜː]&#xff0c;美[dbsɪŋkɜː 简称dbs&#xff09;是一款开源的数据同步中间件&#xff0c;提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步场景。支持上传插件自定义同步转换业务&#xf…...

虚幻5描边轮廓材质

很多游戏内都有这种描边效果&#xff0c;挺实用也挺好看的&#xff0c;简单复刻一下 效果演示&#xff1a; Linethickness可以控制轮廓线条的粗细 这样连完&#xff0c;然后放到网格体细节的覆层材质上即可 可以自己更改粗细大小和颜色...

ISP帳戶會記錄什麼資訊?

許多用戶並不知道ISP會記錄有關線上活動的大量資訊。從流覽歷史記錄到數據使用情況&#xff0c;ISP經常收集和保留用戶數據&#xff0c;引發一系列隱私問題。 ISP 記錄哪些數據&#xff1f; ISP可以根據其隱私政策記錄各種類型的資訊。常見的記錄數據包括&#xff1a; 1.流覽…...

Facebook如何避免因IP变动而封号?实用指南

随着Facebook在个人社交与商业推广中的广泛应用&#xff0c;越来越多的用户面临因“IP变动”而被封号的问题。尤其是跨境电商、广告运营者和多账号管理用户&#xff0c;这种情况可能严重影响正常使用和业务发展。那么&#xff0c;如何避免因IP变动导致的封号问题&#xff1f;本…...

EXCEL数据清洗的几个功能总结备忘

目录 0 参考教材 1 用EXCEL进行数据清洗的几个功能 2 删除重复值&#xff1a; 3 找到缺失值等 4 大小写转换 5 类型转化 6 识别空格 0 参考教材 精通EXCEL数据统计与分析&#xff0c;中国&#xff0c;李宗璋用EXCEL学统计学&#xff0c;日EXCEL统计分析与决策&#x…...

web网页连接MQTT,显示数据与下发控制命令

web网页连接MQTT&#xff0c;显示数据与下发控制命令 零、前言 在完成一些设备作品后&#xff0c;常常会因为没有一个上位机用来实时检测数据和下发命令而苦恼&#xff0c;在上一篇文章中提到了怎么白嫖阿里云服务器&#xff0c;并且在上面搭建了属于自己的web网站。那么现在…...

数据结构day3作业

一、完整功能【顺序表】的创建 【seqList.h】 #ifndef __SEQLIST_H__ #define __SEQLIST_H__#include <stdio.h> #include <string.h> #include <stdlib.h>//宏定义&#xff0c;线性表的最大容量 #define MAX 30//类型重定义&#xff0c;表示要存放数据的类…...

Android SDK 平台工具版本说明

Android SDK Platform-Tools 是 Android SDK 的一个组件。它包含与 Android 平台进行交互的工具&#xff0c;主要是 adb 和 fastboot。虽然 adb 是 Android 应用开发所必需的&#xff0c;但应用开发者通常仅使用 Studio 安装的副本。如果您想直接从命令行使用 adb 并且未安装 S…...

Sharding-jdbc基本使用步骤以及执行原理剖析

一、基本使用步骤 1、需求说明 使用sharding-jdbc完成对订单表的水平分表&#xff0c;通过快速入门的开发&#xff0c;了解sharding-jdbc使用方法 人工创建两张表&#xff0c;t_order_1和t_order_2&#xff0c;这两张表是订单表拆分后的表&#xff0c;通过sharding-jdbc向订…...

mysql重置root密码(适用于5.7和8.0)

今天出一期重置mysql root密码的教程&#xff0c;适用于5.7和8.0&#xff0c;在网上搜索了很多的教程发现都没有效果&#xff0c;浪费了很多时间&#xff0c;尝试了多次之后发现这种方式是最稳妥的&#xff0c;那么废话不多说&#xff0c;往下看&#xff1a; 目录 第一步&…...

Linux下SVN客户端保存账号密码

参考文章&#xff1a;解决&#xff1a;Linux上SVN 1.12版本以上无法直接存储明文密码_linux svn 保存密码-CSDN博客新版本svn使用gpg-agent存储密码-CSDN博客svn之无法让 SVN 存储密码&#xff0c;即使配置设置为允许_编程设计_ITGUEST 方法一&#xff1a;明文方式保存密码 首…...

centos7.9 gcc升级到11.2.1

一、信息查看 # cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) # gcc --version gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44) Copyright © 2015 Free Software Foundation, Inc. 本程序是自由软件&#xff1b;请参看源代码的版权声明。本软件没有任…...

HQChart使用教程30-K线图如何对接第3方数据42-DRAWTEXTREL,DRAWTEXTABS数据结构

HQChart使用教程30-K线图如何对接第3方数据42-DRAWTEXTREL,DRAWTEXTABS数据结构 效果图DRAWTEXTREL示例数据结构说明nametypecolorDrawVAlignDrawAlignDrawDrawTypeDrawDataFont DRAWTEXTABS示例数据结构说明nametypecolorDrawVAlignDrawAlignDrawDrawTypeDrawDataFont 效果图 …...

数仓高频面试 | 数仓为什么要分层

大家好&#xff0c;我是大D呀。 关于数仓分层&#xff0c;在面试过程中几乎是必问的。不过&#xff0c;面试官一般也不会直接考你数仓为什么要分层&#xff0c;而是在你介绍项目时&#xff0c;可能会换一种形式来穿插着问&#xff0c;比如数据链路为什么要这样设计&#xff0c…...

网络安全—部署CA证书服务器

网络拓扑 两台服务器在同一网段即可&#xff0c;即能够互相ping通。 安装步骤 安装证书系统 首先我们对计算机名进行确认&#xff0c;安装了证书系统后我们是不能随意更改计算机名字的&#xff0c;因为以后颁发的证书都是和计算机也就是这一台的服务器名字有关。 修改完成后开…...

MATLAB中circshift函数的原理分析——psf2otf函数的核心

之所以讲到MATLAB中circshift函数&#xff0c;也是源于Rafael Gonzalez的这个图&#xff0c;作为前几篇答廖老师问的blog的基础。 Rafael Gonzalez的这个图无论从哪幅图到哪幅图都不是直接的傅里叶变换或傅里叶逆变换&#xff0c;需要循环移位&#xff0c;即circshift函数。 这…...

做网站收入怎样/阿里域名购买网站

人说伟大的旅程都是从第一篇博文开始的&#xff01; 今天我也在博客园属于我自己的博客上写下了这些文字&#xff0c;并要求自己持续更新&#xff0c;希望籍此鞭策自己不断前行… 又逢一年一度的又十一&#xff0c; 由于闲得无聊&#xff0c; 打开了QQ空间的私密日志&#xff…...

专做坏消息的网站/seo外包公司优化

如果已经求得各点坐标,或者说我们说的,能够建系,就用“法向量法”,所谓法向量,是指垂直于一个平面的直线,根据向量可在平面内任意平移,我们可以知道,一个平面的法向量有无数多条.以上是理论知识简介,因不知道你懂不,所以只得在此阐述下,不然可能会对下面的问题的理解不透产生障…...

wordpress文件锁定了/深圳网络运营推广公司

文章目录常见数据模型问题解决关系型数据库设计范式问题概念理解常见数据模型 问题 数据的存储形式 解决 层次模型 有向树结构 网状模型 有向图结构 关系模型 二维表结构 关系型数据库设计范式 问题 提高数据库的利用率,减少冗余 概念 数据库范式&#xff08;1NF 2NF…...

做网站须要什么技术/百度爱采购怎样入驻

0x00 简介 元组&#xff08;tuple&#xff09;&#xff0c;相当于不可变的列表。元组通过圆括号中用逗号分割的项目定义。 特点&#xff1a;1、有序的集合&#xff1b;2、通过偏移来取数据&#xff1b;3、属于不可变对象&#xff0c;不能在于原地修改内容&#xff0c;没有排序。…...

wordpress mobile 主题/北京关键词seo

(1) 进入目录手动删除 configure作用:是源码安装软件时配置环境用的 他根据你的配置选项和你的系统情况生成makefile文件 为make 做准备 最常用的参数: ./configure --prefix 作用:不指定prefix&#xff0c;则可执行文件默认放在/usr/local/bin&#xff0c;库文件默认放在/usr/…...

学网站建设好吗/免费建站的网站

Mysql的if既可以作为表达式用&#xff0c;也可在存储过程中作为流程控制语句使用&#xff0c;如下是做为表达式使用&#xff1a;IF表达式IF(expr1,expr2,expr3)如果 expr1 是TRUE (expr1 <> 0 and expr1 <> NULL)&#xff0c;则 IF()的返回值为expr2; 否则返回值则…...