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

鸿蒙多线程应用-taskPool

 并发模型

      并发模型是用来实现不同应用场景中并发任务的编程模型,常见的并发模型分为基于内存共享的并发模型和基于消息通信的并发模型。

        Actor并发模型作为基于消息通信并发模型的典型代表,不需要开发者去面对锁带来的一系列复杂偶发的问题,同时并发度也相对较高,因此得到了广泛的支持和使用。

       当前鸿蒙ArkTS提供了TaskPool和Worker两种并发能力,TaskPool和Worker都基于Actor并发模型实现。

       内存共享并发模型指多线程同时执行任务,这些线程依赖同一内存并且都有权限访问,线程访问内存前需要抢占并锁定内存的使用权,没有抢占到内存的线程需要等待其他线程释放使用权再执行。

       Actor并发模型每一个线程都是一个独立Actor,每个Actor有自己独立的内存,Actor之间通过消息传递机制触发对方Actor的行为,不同Actor之间不能直接访问对方的内存空间。Actor并发模型对比内存共享并发模型的优势在于不同线程间内存隔离,不会产生不同线程竞争同一内存资源的问题。开发者不需要考虑对内存上锁导致的一系列功能、性能问题,提升了开发效率。

       由于Actor并发模型线程之间不共享内存,需要通过线程间通信机制传输并发任务和任务结果。

TaskPool简介

      任务池(TaskPool)作用是为应用程序提供一个多线程的运行环境,降低整体资源的消耗、提高系统的整体性能,且您无需关心线程实例的生命周期。

     TaskPool支持开发者在宿主线程封装任务抛给任务队列,系统选择合适的工作线程,进行任务的分发及执行,再将结果返回给宿主线程。接口直观易用,支持任务的执行、取消,以及指定优先级的能力,同时通过系统统一线程管理,结合动态调度及负载均衡算法,可以节约系统资源。系统默认会启动一个任务工作线程,当任务较多时会扩容,工作线程数量上限跟当前设备的物理核数相关,具体数量内部管理,保证最优的调度及执行效率,长时间没有任务分发时会缩容,减少工作线程数量。

TaskPool注意事项

  • 实现任务的函数需要使用@Concurrent装饰器标注,且仅支持在.ets文件中使用。

  • 从API version 11开始,跨并发实例传递带方法的实例对象时,该类必须使用装饰器@Sendable装饰器标注,且仅支持在.ets文件中使用。

  • 任务函数在TaskPool工作线程的执行耗时不能超过3分钟(不包含Promise和async/await异步调用的耗时,例如网络下载、文件读写等I/O任务的耗时),否则会被强制退出。

  • 实现任务的函数入参需满足序列化支持的类型,详情请参见线程间通信对象。

  • ArrayBuffer参数在TaskPool中默认转移,需要设置转移列表的话可通过接口setTransferList()设置。

  • 由于不同线程中上下文对象是不同的,因此TaskPool工作线程只能使用线程安全的库,例如UI相关的非线程安全库不能使用。

  • 序列化传输的数据量大小限制为16MB。

  • Priority的IDLE优先级是用来标记需要在后台运行的耗时任务(例如数据同步、备份),它的优先级别是最低的。这种优先级标记的任务只会在所有线程都空闲的情况下触发执行,并且只会占用一个线程来执行。

  • Promise不支持跨线程传递,如果TaskPool返回pending或rejected状态的Promise,会返回失败;对于fulfilled状态的Promise,TaskPool会解析返回的结果,如果结果可以跨线程传递,则返回成功。

  • 不支持在TaskPool工作线程中使用AppStorage。

TaskPool应用实例

       生产者消费者模型应用taskPool的具体代码实现

1.生产者


import { taskpool } from '@kit.ArkTS';
import { stingToUint8, uint8TransformString } from './utils';@Concurrent
export async function producer(ArrayBuffer: Int32Array, dataBuffer: Uint8Array, newStr: string) {let i32a = ArrayBuffer;let array = dataBufferif (array[array.length-1] !== 0) {taskpool.Task.sendData(false)let runner = new taskpool.SequenceRunner()console.log("-----atomics-producer-push-fal-" + newStr)return}let jsonStr: string = uint8TransformString(array)let arr: string[] = []try {arr= JSON.parse(jsonStr) as string[]} catch (e) {taskpool.Task.sendData(false)return}arr.push(newStr)let newArrJson = JSON.stringify(arr) ?? ''//console.log("newArrJson" + newArrJson)let isFinish = stingToUint8(newArrJson,array,4)if (!isFinish) {arr.pop()let newArrJson1 = JSON.stringify(arr) ?? ''stingToUint8(newArrJson1,array,4)taskpool.Task.sendData(false)console.log("-----atomics-producer-push-fal-" + newStr)}else{console.log("-----atomics-producer-push-sec-" + newStr)}Atomics.notify(i32a, 0, 1)Promise.resolve()
}

2.消费者


import { getStringArrayFromJson, testMethod, uint8TransformString} from './utils';
import { buffer, taskpool } from '@kit.ArkTS';
import { ThreadUtils } from './ThreadUtils';@Concurrent
export async function consumerTask(ArrayBuffer: Int32Array, dataBuffer: Uint8Array): Promise<void> {let i32a = ArrayBuffer;let array = dataBufferwhile (true) {let jsonStr: string = uint8TransformString(array)let arr = getStringArrayFromJson(jsonStr)if (arr.length == 0) {Atomics.wait(i32a, 0, 0);} else {let i = 4for (let index = 0; index < array.byteLength; index++) {if (i >= array.byteLength) {break}Atomics.store(array, i++, 0)}taskpool.Task.sendData(true)let writeResult: boolean = truewhile ((writeResult == true || writeResult == false)) {let ele = arr.shift()if (!ele) {break}writeResult = await ThreadUtils.getInstance().writeToFile(ele)console.log('-----atomics-consumer-' + ele)}}}
}

3.字符串和字节码相互转换工具

export function testMethod(str: string) {console.log('--test-function-str-' + str)
}
export function uint8TransformString(array:Uint8Array): string{let jsonStr: string = JSON.stringify([])let tempArr: number[] = []let j = 0for (let index = 0; index < array.length; index++) {if (array[index] == 0) {continue}tempArr[j++] = array[index]}let temp = new Uint8Array(tempArr)if (temp.byteLength > 0) {let str = '';for (let i = 0; i < temp.length; ) {let byte1 = temp[i];let codePoint: numberif (byte1 >> 7 === 0) { // 1字节codePoint = byte1;i += 1;} else if (byte1 >> 5 === 0b110) { // 2字节codePoint = ((byte1 & 0b11111) << 6) | (temp[i + 1] & 0b111111);i += 2;} else if (byte1 >> 4 === 0b1110) { // 3字节codePoint = ((byte1 & 0b1111) << 12) | ((temp[i + 1] & 0b111111) << 6) | (temp[i + 2] & 0b111111);i += 3;} else {// 错误处理:不支持的字节序列i += 1; // 跳过当前字节continue;}str += String.fromCodePoint(codePoint)console.info('字节流转成可理解的字符串:' + str);}jsonStr = str}return jsonStr
}
//
export function stingToUint8(json: string, array:Uint8Array,formIndex: number = 0) : boolean{let i = formIndexlet isFinish = truefor (let index = 0; index < json.length; index++) {if (i >= array.byteLength) {if (index < json.length - 1) {isFinish = false}break}const element = json.charCodeAt(index);if (element > 0x7FF) {Atomics.store(array, i++, (0xE0 | (element >> 12)))Atomics.store(array, i++, (0x80 | ((element >> 6) & 0x3F)))Atomics.store(array, i++, (0x80 | (element & 0x3F)))} else if (element > 0x7F) {Atomics.store(array, i++, (0xC0 | (element >> 6)))Atomics.store(array, i++, (0x80 | (element & 0x3F)))} else {Atomics.store(array, i++, (element))}}//剩余空间赋值0for (let index = i; index < array.length; index++) {array[index] = 0}return isFinish
}

4.单例工具

import { taskpool } from '@kit.ArkTS';
import { it } from '@ohos/hypium';
import { consumerTask } from './consumer';
import { producer } from './product';export class ThreadUtils {private tempLogList: Array<string> = new Array()private static instance: ThreadUtilsprivate sab :SharedArrayBufferprivate ui8 :Uint8Arrayprivate i32a :Int32Arrayprivate constructor(bufferSize:number = 1024) {this.sab = new SharedArrayBuffer(bufferSize)this.ui8 = new Uint8Array(this.sab)this.i32a = new Int32Array(this.sab)this.startConsumer()};writeLog(log: string) {if (this.flag) {this.tempLogList.push(log)}else {this.product(log)}}public static getInstance(bufferSize:number = 1024): ThreadUtils {if (!ThreadUtils.instance) {ThreadUtils.instance = new ThreadUtils(bufferSize);}return ThreadUtils.instance;}async writeToFile(content: string): Promise<boolean> {return new Promise((resolve, reject) => {setTimeout(() => {console.log("日志写入完成=" + content)console.log('pop element=' + content)resolve(true)}, 4000)})}lastTask:taskpool.Task | undefinedflag = falseasync product(log: string):Promise<boolean> {return new Promise<boolean>((resolve,reject)=>{let newLog = loglet task = new taskpool.Task(producer, this.i32a, this.ui8, newLog)if (this.lastTask) {task.addDependency(this.lastTask)}this.lastTask = tasktask.onReceiveData((success: boolean) => {if (!success) {this.flag = truethis.tempLogList.unshift(log)resolve(false)}})taskpool.execute(task).then(()=>{console.log('------taskpool.execute.then-----')resolve(true)});})}isWhile = falseasync startConsumer() {let task = new taskpool.Task(consumerTask, this.i32a, this.ui8)task.onReceiveData(async (hasSpace: boolean) => {if (hasSpace) {this.flag = falseif (this.tempLogList.length > 0 && this.isWhile == false){let item = this.tempLogList.shift()console.log('---item---'+ item)this.isWhile = truelet com = truewhile (item && this.flag == false && com){com = await this.product(item)item = this.tempLogList.shift()}this.isWhile = false}}})taskpool.execute(task)}}

5.页面UI应用

import { buffer, taskpool } from '@kit.ArkTS';
import { consumerTask } from '../consumer';
import { producer } from '../product';
import { router } from '@kit.ArkUI';
import { ThreadUtils } from '../ThreadUtils';@Entry
@Component
struct Index {timer = -1count = 0logTool = ThreadUtils.getInstance(32)aboutToAppear(): void {}@State inputText:string =''build() {Column({space: 20}) {TextInput({text: $$this.inputText}).width('80%')Button() {Text("生产日志").padding(10)}.backgroundColor(Color.Gray).onClick(async () => {this.timer = setInterval(()=>{this.logTool.writeLog ('item' + this.count)this.count += 1},1000)})Button() {Text("停止生产").padding(10)}.backgroundColor(Color.Gray).onClick(async () => {clearInterval(this.timer)// router.pushUrl({//   url: 'pages/TaskPoolPage'// })})}.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center).height('100%').width('100%')}
}

相关文章:

鸿蒙多线程应用-taskPool

并发模型 并发模型是用来实现不同应用场景中并发任务的编程模型&#xff0c;常见的并发模型分为基于内存共享的并发模型和基于消息通信的并发模型。 Actor并发模型作为基于消息通信并发模型的典型代表&#xff0c;不需要开发者去面对锁带来的一系列复杂偶发的问题&#xff0c;同…...

【失败经验】将算法模型封装为安卓应用

背景&#xff1a;不懂安卓开发&#xff0c;希望能使用大模型编码完成安卓应用生成&#xff0c;调用算法模型进行预测。 模型准备&#xff1a; pip方案安装pcnn&#xff1b; 然后需要将pytorch训练完成的算法模型保存为torchscript模型&#xff0c;然后使用pcnn转换为ncnn的模…...

ABAP OOALV模板

自用模板&#xff0c;可能存在问题 一、主程序 *&---------------------------------------------------------------------* *& Report ZVIA_OO_ALV *&---------------------------------------------------------------------* REPORT ZVIA_OO_ALV.INCLUDE ZVI…...

YOLOv8-ultralytics-8.2.103部分代码阅读笔记-autobatch.py

autobatch.py ultralytics\utils\autobatch.py 目录 autobatch.py 1.所需的库和模块 2.def check_train_batch_size(model, imgsz640, ampTrue, batch-1): 3.def autobatch(model, imgsz640, fraction0.60, batch_sizeDEFAULT_CFG.batch): 1.所需的库和模块 # Ultraly…...

SycoTec 4060 ER-S德国高精密主轴电机如何支持模具的自动化加工?

SycoTec 4060 ER-S高速电主轴在模具自动化加工中的支持体现在以下几个关键方面&#xff1a; 1.高精度与稳定性&#xff1a;SycoTec 4060 ER-S锥面跳动小于1微米&#xff0c;确保了加工过程中的极高精度&#xff0c;这对于模具的复杂几何形状和严格公差要求至关重要。高精度加工…...

部署 DeepSpeed以推理 defog/sqlcoder-70b-alpha 模型

部署 DeepSpeed 以推理 defog/sqlcoder-70b-alpha 这样的 70B 模型是一个复杂的过程&#xff0c;涉及多个关键步骤。下面是详细的步骤&#xff0c;涵盖了从模型加载、内存优化到加速推理的全过程。 1. 准备环境 确保你的环境配置正确&#xff0c;以便能够顺利部署 defog/sqlc…...

Python网络爬虫基础

Python网络爬虫是一种自动化工具&#xff0c;用于从互联网上抓取信息。它通过模拟人类浏览网页的行为&#xff0c;自动地访问网站并提取所需的数据。网络爬虫在数据挖掘、搜索引擎优化、市场研究等多个领域都有广泛的应用。以下是Python网络爬虫的一些基本概念&#xff1a; 1.…...

每天五分钟机器学习:支持向量机数学基础之超平面分离定理

本文重点 超平面分离定理(Separating Hyperplane Theorem)是数学和机器学习领域中的一个重要概念,特别是在凸集理论和最优化理论中有着广泛的应用。该定理表明,在特定的条件下,两个不相交的凸集总可以用一个超平面进行分离。 定义与表述 超平面分离定理(Separating Hy…...

TCP/IP网络协议栈

TCP/IP网络协议栈是一个分层的网络模型&#xff0c;用于在互联网和其他网络中传输数据。它由几个关键的协议层组成&#xff0c;每一层负责特定的功能。以下是对TCP/IP协议栈的简要介绍&#xff1a; TCP/IP协议模型的分层 1. 应用层&#xff08;Application Layer&#xff09;…...

利用编程思维做题之最小堆选出最大的前10个整数

1. 理解问题 我们需要设计一个程序&#xff0c;读取 80,000 个无序的整数&#xff0c;并将它们存储在顺序表&#xff08;数组&#xff09;中。然后从这些整数中选出最大的前 10 个整数&#xff0c;并打印它们。要求我们使用时间复杂度最低的算法。 由于数据量很大&#xff0c;直…...

详解MVC架构与三层架构以及DO、VO、DTO、BO、PO | SpringBoot基础概念

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 今天毛毛张分享的是SpeingBoot框架学习中的一些基础概念性的东西&#xff1a;MVC结构、三层架构、POJO、Entity、PO、VO、DO、BO、DTO、DAO 文章目录 1.架构1.1 基本…...

Unity C# 影响性能的坑点

c用的时间长了怕unity的坑忘了&#xff0c;记录一下。 GetComponent最好使用GetComponent<T>()的形式&#xff0c; 继承自Monobehaviour的函数要避免空的Awake()、Start()、Update()、FixedUpdate().这些空回调会造成性能浪费 GetComponent方法最好避免在Update当中使用…...

工作学习:切换git账号

概括 最近工作用的git账号下发下来了&#xff0c;需要切换一下使用的账号。因为是第一次弄&#xff0c;不熟悉&#xff0c;现在记录一下。 打开设置 路径–git—git remotes&#xff0c;我这里选择项是Manage Remotes&#xff0c;点进去就可以了。 之后会出现一个输入框&am…...

量化交易系统开发-实时行情自动化交易-8.量化交易服务平台(一)

19年创业做过一年的量化交易但没有成功&#xff0c;作为交易系统的开发人员积累了一些经验&#xff0c;最近想重新研究交易系统&#xff0c;一边整理一边写出来一些思考供大家参考&#xff0c;也希望跟做量化的朋友有更多的交流和合作。 接下来会对于收集整理的33个量化交易服…...

Scala习题

姓名&#xff0c;语文&#xff0c;数学&#xff0c;英语 张伟&#xff0c;87&#xff0c;92&#xff0c;88 李娜&#xff0c;90&#xff0c;85&#xff0c;95 王强&#xff0c;78&#xff0c;90&#xff0c;82 赵敏&#xff0c;92&#xff0c;88&#xff0c;91 孙涛&#xff0c…...

结构方程模型(SEM)入门到精通:lavaan VS piecewiseSEM、全局估计/局域估计;潜变量分析、复合变量分析、贝叶斯SEM在生态学领域应用

目录 第一章 夯实基础 R/Rstudio简介及入门 第二章 结构方程模型&#xff08;SEM&#xff09;介绍 第三章 R语言SEM分析入门&#xff1a;lavaan VS piecewiseSEM 第四章 SEM全局估计&#xff08;lavaan&#xff09;在生态学领域高阶应用 第五章 SEM潜变量分析在生态学领域…...

OpenCV图像基础处理:通道分离与灰度转换

在计算机视觉处理中&#xff0c;理解图像的颜色通道和灰度表示是非常重要的基础知识。今天我们通过Python和OpenCV来探索图像的基本组成。 ## 1. 图像的基本组成 在数字图像处理中&#xff0c;彩色图像通常由三个基本颜色通道组成&#xff1a; - 蓝色&#xff08;Blue&#x…...

C++ 类和对象(类型转换、static成员)

目录 一、前言 二、正文 1.隐式类型转换 1.1隐式类型转换的使用 2.static成员 2.1 static 成员的使用 2.1.1static修辞成员变量 2.1.2 static修辞成员函数 三、结语 一、前言 大家好&#xff0c;我们又见面了。昨天我们已经分享了初始化列表&#xff1a;https://blog.c…...

【网络安全设备系列】12、态势感知

0x00 定义&#xff1a; 态势感知&#xff08;Situation Awareness&#xff0c;SA&#xff09;能够检测出超过20大类的云上安全风险&#xff0c;包括DDoS攻击、暴力破解、Web攻击、后门木马、僵尸主机、异常行为、漏洞攻击、命令与控制等。利用大数据分析技术&#xff0c;态势感…...

Linux介绍与安装指南:从入门到精通

1. Linux简介 1.1 什么是Linux&#xff1f; Linux是一种基于Unix的操作系统&#xff0c;由Linus Torvalds于1991年首次发布。Linux的核心&#xff08;Kernel&#xff09;是开源的&#xff0c;允许任何人自由使用、修改和分发。Linux操作系统通常包括Linux内核、GNU工具集、图…...

BGE-M3模型结合Milvus向量数据库强强联合实现混合检索

在基于生成式人工智能的应用开发中&#xff0c;通过关键词或语义匹配的方式对用户提问意图进行识别是一个很重要的步骤&#xff0c;因为识别的精准与否会影响后续大语言模型能否检索出合适的内容作为推理的上下文信息&#xff08;或选择合适的工具&#xff09;以给出用户最符合…...

鸿蒙NEXT开发案例:文字转拼音

【引言】 在鸿蒙NEXT开发中&#xff0c;文字转拼音是一个常见的需求&#xff0c;本文将介绍如何利用鸿蒙系统和pinyin-pro库实现文字转拼音的功能。 【环境准备】 • 操作系统&#xff1a;Windows 10 • 开发工具&#xff1a;DevEco Studio NEXT Beta1 Build Version: 5.0.…...

CTF之密码学(栅栏加密)

栅栏密码是古典密码的一种&#xff0c;其原理是将一组要加密的明文划分为n个一组&#xff08;n通常根据加密需求确定&#xff0c;且一般不会太大&#xff0c;以保证密码的复杂性和安全性&#xff09;&#xff0c;然后取每个组的第一个字符&#xff08;有时也涉及取其他位置的字…...

修改插槽样式,el-input 插槽 append 的样式

需缩少插槽 append 的 宽度 方法1、使用内联样式直接修改&#xff0c;指定 width 为 30px <el-input v-model"props.applyBasicInfo.outerApplyId" :disabled"props.operateCommandType input-modify"><template #append><el-button click…...

UPLOAD LABS | PASS 01 - 绕过前端 JS 限制

关注这个靶场的其它相关笔记&#xff1a;UPLOAD LABS —— 靶场笔记合集-CSDN博客 0x01&#xff1a;过关流程 本关的目标是上传一个 WebShell 到目标服务器上&#xff0c;并成功访问&#xff1a; 我们直接尝试上传后缀为 .php 的一句话木马&#xff1a; 如上&#xff0c;靶场弹…...

【css实现收货地址下边的平行四边形彩色线条】

废话不多说&#xff0c;直接上代码&#xff1a; <div class"address-block" ><!-- 其他内容... --><div class"checked-ar"></div> </div> .address-block{height:120px;position: relative;overflow: hidden;width: 500p…...

缓存方案分享

不知道大家平常更新缓存是怎么做的&#xff0c;但是大部分时候都是更新数据的同时更新缓存&#xff0c;今天和同事一起聊到一个缓存方案的问题&#xff0c;感觉很有趣、非常精妙&#xff0c;记录一下。 基于此本文将介绍几种常见的缓存更新策略&#xff0c;包括简单的缓存覆盖…...

第四十篇 DDP模型并行

摘要 分布式数据并行(DDP)技术是深度学习领域中的一项重要技术,它通过将数据和计算任务分布在多个计算节点上,实现了大规模模型的并行训练。 DDP技术的基本原理是将数据和模型参数分割成多个部分,每个部分由一个计算节点负责处理。在训练过程中,每个节点独立计算梯度,…...

软件测试面试之常规问题

1.描述一下测试过程 类似题目:测试的生命周期 思路:这是一个“范围”很大的题目&#xff0c;而且回答时间一般在3分钟之内&#xff0c;不可能非常详细的描述整个过程&#xff0c;因此答题的思路要从整体结构入手&#xff0c;不要过细。为了保证答案的准确性&#xff0c;可以引…...

《图像形态学运算全解析:原理、语法及示例展示》

简介&#xff1a; 本文详细介绍了图像形态学中的多种运算&#xff0c;包括腐蚀、膨胀、开运算、闭运算、形态学梯度运算、礼帽运算以及黑帽运算。分别阐述了各运算的原理、语法格式&#xff0c;并通过 Python 代码结合具体示例图片&#xff08;如erode.JPG、dilate.JPG、close.…...

eclipse sdk做网站/微营销推广方案

1,从spark的example中找到KafkaWordCount.scala文件复制到idea编辑器中,引入包:2,编辑configuration,(1)KafkaWordCountProducer选择KafkaWordCount.scala中的KafkaWordCountProducer方法VM options 设置为:-Dspark.masterlocal设置程序输入参数,Program arguments: localhost:…...

建筑网官网软件/泉州seo报价

符号键&#xff08;CTRL开头&#xff09; CTRL1 PROPCLOSEOROPEN 对象特性管理器CTRL2或4 ADCENTER 设计中心CTRL3 CTOOLPALETTES 工具选项板CTRL8或QC QuickCalc 快速计算器控制键CTRLA AI_SELALL 全部选择CTRLC或CO/CP COPYCLIP或COpy 复制CTRLD或F6 COORDINATE 坐标CTRLE或F…...

政府网站建设专题的目的/免费发外链

上一期我对BBR进行修改的目标很简单&#xff0c;就是提高算法的RTT公平性&#xff0c;主要修改点有3个&#xff1a; 固定增益系数改为了RTT的减函数。RTT相关的steady phase改为了固定时间。探测到带宽腾出时&#xff0c;马上进行up probe。 详情参见&#xff1a; https://zh…...

广东网站设计的公司/推广引流方法有哪些推广方法

今天为大家带来的内容比较实用&#xff0c;主要还是针对零基础的小伙伴&#xff0c;话不多说&#xff0c;直接开始码&#xff08;本文内容用的是第一人称&#xff09; 平常我都是直接执行 pip install 安装的第三方库&#xff0c;很多教程也是这么介绍的&#xff0c;一直以来我…...

简单网站制作教程/超链接友情外链查询

删除 node_modules包 然后npm install 然后再deploy d即可 这个问题真是闹心死我了 https://github.com/npm/npm/issues/17444 网上又有人说是vs code占用了&#xff0c;所以最好把vs code也暂时关掉 我这样做之后就好使了 还有说加环境变量的&#xff0c;也就是把博客地址…...

品牌网站建设相关问题/百度权重10的网站

前言&#xff1a;Wire.h是Arduino的IIC库。 一、Wire库函数 Wire.begin()Wire.requestFrom()Wire.beginTransmission()Wire.endTransmission()Wire.write()Wire.available()Wire.read()Wire.onReceive()Wire.onRequest()二、库函数详细介绍 1、Wire.begin() 和 Wire.begin(addr…...