Linux -- 线程控制相关的函数
目录
pthread_create -- 创建线程
参数
返回值
代码 -- 不传 args:
编译时带 -lpthread
运行结果
为什么输出混杂?
如何证明两个线程属于同一个进程?
如何证明是两个执行流?
什么是LWP?
代码 -- 传 args:
运行结果:
pthread_self -- 线程标识符
代码:
LWP标识符 和 线程标识符的区别
pthread_join -- 等待线程退出
前言:主线程比新线程先退出
参数
返回值
代码 -- 不获取退出状态
代码 -- 获取退出状态
编辑
pthread_exit -- 终止线程
前言:新、主线程共享地址空间
参数
作用
代码
pthread_cancel -- 取消线程
参数
返回值
代码
pthread_create -- 创建线程
#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数
thread
: 指向一个pthread_t
类型的指针,用于存储新创建线程的标识符,是输出型参数。
attr
: 指向一个pthread_attr_t
类型的指针,这个参数可以用来设置线程的属性,如栈大小、调度策略等。如果不需要特别设置线程属性,可以传递NULL
。
start_routine
: 这是一个函数指针,指向新线程开始执行的函数。该函数必须接受一个void*
类型的参数,并返回一个void*
类型的结果。
arg
: 这个参数将被传递给start_routine
函数作为其唯一的参数。如果你不想传递任何参数,可以使用NULL
。
返回值
如果函数调用成功,返回值为
0
。
如果发生错误,返回值为一个非零的错误代码。
代码 -- 不传 args:
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;void* newthreadRun(void* args)
{while(1){cout<<"I am new thread"<<endl;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,nullptr);while(1){cout<<"I am main thread"<<endl;sleep(1);}return 0;
}
编译时带 -lpthread
这里需要了解一点小故事,用户知道线程和进程,但不知道轻量级进程,而Linux中没有真线程,Linux中没有线程相关的系统调用,只有轻量级进程的系统调用,为了让用户和系统达成一致,系统将轻量级进程的系统调用进行封装,转换成线程相关的接口语义提供给用户,也就有了pthread库(即原生线程库),这个库既不属于C语言,也不属于C++,所以在Linux系统中编写多线程时,都必须在编译时带上 -lpthread。
在 Linux 中编译使用 Pthreads 的程序时,通常需要链接 Pthreads 库。这可以通过在编译命令中添加
-lpthread
选项来实现。-lpthread
选项不仅告诉编译器链接 Pthreads 库,还会启用一些必要的编译器选项,以确保线程支持的正确性和性能。 如果不使用-lpthread
选项,编译器可能会报错或生成不可用的二进制文件。
thread:thread.ccg++ -o $@ $^ -std=c++11 -lpthread.PHONY:clean
clean:rm -f thread
运行结果
可以看出,两个执行流同时输出信息。同时也可以看出,一开始新、主线程打印的消息混在一起了,后面才分开来,这是正常现象。
为什么输出混杂?
在多线程程序中,多个线程同时向终端输出信息时,可能会出现输出混杂的情况。这是因为每个线程的输出操作并不是原子的(原子操作,即要么完全执行,要么根本不执行),即一个线程可能在另一个线程已经开始输出但还没有完成输出时就开始了自己的输出,这种现象通常称为“输出交错”或“输出混杂”。
如何证明两个线程属于同一个进程?
当不传 args 版本的代码运行时,输入命令 ps ajx | head -1 && ps ajx | grep thread 筛选出 thread 进程,可以看出只有一个进程被调度:
如何证明是两个执行流?
当代码运行起来时,输入命令 ps -aL | head -1 && ps -aL | grep thread 可以查看线程的信息,可以看出两个线程的 pid 一样,即属于同一个进程,而 LWP 不同,则说明一个进程中有两个执行流:
ps -aL
:-a
显示所有进程,包括其他用户的进程,-L 显示每个线程的详细信息。
什么是LWP?
LWP(Light Weight Process)是轻量级进程的缩写,在 Linux 中,LWP 通常被称为“线程”。
代码 -- 传 args:
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(cnt--){std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");pthread_join(tid,nullptr);return 0;
}
运行结果:
pthread_self -- 线程标识符
#include <pthread.h>
pthread_t pthread_self(void);
该函数用于获取当前线程的标识符(
pthread_t
类型)。
代码:
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{while(1){cout<<"I am new thread, new thread tid: "<<toHex(pthread_self())<<", pid: "<<getpid()<<endl;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,nullptr);while(1){cout<<"I am main thread, main thread tid: "<<toHex(pthread_self())<<", pid: "<<getpid()<<endl;sleep(1);}return 0;
}
可以看出,新、主线程的线程标识符 tid 的值不一样,同时也可以看出,LWP 和线程标识符 tid的值是不一样的:
LWP标识符 和 线程标识符的区别
LWP(Light Weight Process)标识符和线程标识符(Thread Identifier,TID)在数值上通常是不一样的。虽然它们在概念上密切相关,但它们表示的是不同的标识符,用途和获取方式也有所不同。
pthread_join -- 等待线程退出
前言:主线程比新线程先退出
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;//新线程运行5秒while(cnt--){std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");sleep(1);//因为主线程没有阻塞等待新线程,1秒后,主线程先退出了std::cout<<"main thread quit"<<std::endl;return 0;
}
因为主线程没有阻塞等待新线程退出,1秒后,主线程退出了,主线程退出了就等同于整个进程退出了,分配给进程的资源都被释放了,所以所有的线程都要退出,所以新线程还没执行完就被退出了,通常需要主线程最后结束。线程的退出也需要wait,不然会发生内存泄漏问题。
#include <pthread.h>int pthread_join(pthread_t thread, void **value_ptr);
该函数用于等待指定的线程终止,并获取该线程的退出状态。
参数
thread
:要等待的线程的标识符(pthread_t
类型,可以调用 pthread_self 函数获取)。
value_ptr
:输出型参数,用于存储线程的退出状态。如果不需要获取退出状态,可以传递NULL
。
返回值
等待线程退出成功,返回
0
。
等待线程退出失败,返回非零错误码。
代码 -- 不获取退出状态
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(cnt--){std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");int n=pthread_join(tid,nullptr);//获取返回值std::cout<<"main thread quit, n="<<n<<std::endl;return 0;
}
新线程正常退出,故返回值为 0.
代码 -- 获取退出状态
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(cnt--){std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;sleep(1);}return (void*)123;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");void* ret=nullptr;int n=pthread_join(tid,&ret);//ret强转为long long是为了避免精度损失std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;return 0;
}
线程的退出状态其实就是线程执行的任务函数的返回值。
pthread_exit -- 终止线程
前言:新、主线程共享地址空间
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(cnt--){printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);g_val++;//在新线程中改变g_val的值sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");int cnt=10;while(cnt--){//主线程不改变g_val的值printf("main thread, g_val:%d,&g_val:%p\n",g_val,&g_val);sleep(1);}void* ret=nullptr;int n=pthread_join(tid,&ret);std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;return 0;
}
可以看出,新线程修改了 g_val 的值,主线程中 g_val 的值也被修改了,说明新、主线程共享了地址空间,看到的是同一个变量,而不是和进程一样,发生写时拷贝。
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(cnt--){printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);g_val++;//故意对空指针进行解引用int *p=nullptr;*p=10;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");int cnt=10;while(cnt--){printf("main thread, g_val:%d,&g_val:%p\n",g_val,&g_val);sleep(1);}void* ret=nullptr;int n=pthread_join(tid,&ret);std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;return 0;
}
在新线程中故意对野指针进行解引用,结果新、主线程一起退出了,这是因为在同一个进程中运行的所有线程共享相同的地址空间,这意味着如果一个线程造成了段错误(segmentation fault),那么这个错误会影响到整个进程,而不仅仅是单个线程。操作系统通常会终止整个进程以防止进一步的损坏。
#include <pthread.h>void pthread_exit(void *value_ptr);
参数
value_ptr
:一个指向指针的指针,用于存储线程的退出状态。这个值可以被
pthread_join
函数捕获并使用。如果不需要传递退出状态,可以传递NULL
。
作用
终止当前线程:调用
pthread_exit
的线程会立即终止其执行。
传递退出状态:可以通过
value_ptr
参数传递一个退出状态,这个状态可以被pthread_join
函数捕获。
代码
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(cnt--){printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);g_val++;sleep(1);}pthread_exit((void*)123);
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");void* ret=nullptr;int n=pthread_join(tid,&ret);std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;return 0;
}
pthread_cancel -- 取消线程
#include <pthread.h>int pthread_cancel(pthread_t thread);
参数
thread
:要取消的线程的标识符(pthread_t
类型)。
返回值
取消线程成功,返回
0
。
取消线程失败,返回非零错误码。
代码
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}
void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(cnt--){printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);g_val++;sleep(1);}pthread_exit((void*)123);
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");pthread_cancel(tid);void* ret=nullptr;int n=pthread_join(tid,&ret);std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;return 0;
}
线程的退出状态为 -1,表示线程被取消。
相关文章:

Linux -- 线程控制相关的函数
目录 pthread_create -- 创建线程 参数 返回值 代码 -- 不传 args: 编译时带 -lpthread 运行结果 为什么输出混杂? 如何证明两个线程属于同一个进程? 如何证明是两个执行流? 什么是LWP? 代码 -- 传 args&a…...

基于quasar,只选择年度与月份的组件
为什么要做 quasar是个基于vue的强大的UI开发库,它提供了非常多的组件,比如日期选择。但是有些时候只需要选择到月份就可以了,quasar中没有,所以自己动手写了一个。因为对界面编程我不熟悉,所以,如果你有更…...

健康养生:拥抱生活的艺术
健康养生:拥抱生活的艺术 在快节奏的现代生活中,健康已成为我们最宝贵的财富。健康养生,不仅仅是一种生活方式的选择,更是一种对待生活的态度,它关乎于如何在日常中寻找到平衡,让身心得以滋养,…...

注意力机制+时空特征融合!组合模型集成学习预测!LSTM-Attention-Adaboost多变量时序预测
注意力机制时空特征融合!组合模型集成学习预测!LSTM-Attention-Adaboost多变量时序预测 目录 注意力机制时空特征融合!组合模型集成学习预测!LSTM-Attention-Adaboost多变量时序预测效果一览基本介绍程序设计参考资料 效果一览 基…...

uniapp 微信小程序 均分数据展示
效果图 数据展示,可自行搭配 html <view class"num-wrapper"><view class"num-item" click.stop"routerGo(跳转的地址)"><text class"num">¥{{ 要展示的数据 || 0}}</text><view…...

Nacos 3.0 考虑升级到 Spring Boot 3 + JDK 17 了!
Nacos 由阿里开源,是 Spring Cloud Alibaba 中的一个重要组件,主要用于发现、配置和管理微服务。 由于 Spring Boot 2 的维护已于近期停止,Nacos 团队考虑升级到 Spring Boot 3 JDK 17,目前正在征求意见和建议。 这其实是一件好…...

跟沐神学读论文-论文阅读管理
摘要 近期有读论文的需求,就需要去了解一下论文到底要怎么读,同一个系列之间的论文如何作整理和归纳,之前也有了解过市面上有成熟的论文阅读工具,但是对于学生党来讲没什么性价比,在B站上看到沐神有讲解他的思路Typor…...

Python 参数配置使用 XML 文件的教程 || Python打包 || 模型部署
当配置项存储在外部文件(如 XML、JSON)时,修改配置无需重新编译和发布代码。通过更新 XML 文件即可调整参数,无需更改源代码,从而提升开发效率和代码可维护性。 1. 为什么选择 XML 配置文件 XML 配置文件具有多种优点…...

[SV]如何在UVM环境中使用C Model
在UVM环境中使用C Memory 一、C语言实现Memory 1.1 代码说明 Memory 初始化: memory_init() 函数将内存空间初始化为 0,并初始化互斥锁。AXI 写操作 (axi_write): 检查地址范围是否合法。使用 memcpy 将数据从输入缓冲区写入模拟内存。使用互斥锁保证线程安全。AXI 读操作 …...

十大开源的Cursor AI替代方案
随着AI的兴起,所使用的工具也在不断进步。Cursor AI 作为一个强大的编码助手,已经成为开发人员不可或缺的工具。开源替代方案提供了透明性、个性化和成本效益。本文深入探讨了Cursor AI 的十大开源替代方案,这些方案将丰富您的编码体验&#…...

相机光学(四十六)——镜头马达(VCM)控制策略模式
One Step Mode、Linear Slope Control(LSC)和Acceleration Control是三种不同的控制模式,它们在控制策略和应用场景上有所区别。这些控制模式在VCM中的应用是为了提高其性能,减少振动,加快响应速度,并提高定…...

专业140+总分410+浙江大学842信号系统与数字电路考研经验浙大电子信息与通信工程,真题,大纲,参考书。
考研落幕,本人本中游211,如愿以偿考入浙江大学,专业课842信号系统与数字电路140,总分410,和考前多次模考预期差距不大(建议大家平时做好定期模考测试,直接从实战分数中,找到复习的脉…...

了解ARM的千兆以太网——RK3588
1. 简介 本文并不重点讲解调试内容,重点了解以太网在ARM设计中的框架以及在设备树以及驱动的一个整体框架。了解作为一个驱动开发人员当拿到一款未开发过的ARM板卡应该怎么去把网卡配置使用起来。 2. 基础知识介绍 在嵌入式ARM中实现以太网的解决方案通常有以下两种…...

JavaFX使用jfoenix的UI控件
jfoenix还是一个不错的样式,推荐使用,而且也可以支持scene builder中的拖拖拽拽 需要注意的是过高的javafx版本可能会使得某些样式或控件无法使用 比如alert控件,亲测javaFX 19版本可以正常使用 1.在pom.xml中引入依赖 GitHub地址https://gi…...

Linux(Ubuntu)命令大全——已分类整理,学习、查看更加方便直观!(2024年最新编制)
Hello! 认真好学的小伙伴们,大家好呀(Respect~)!我是 H u a z z i Huazzi Huazzi,欢迎观看本篇博客,接下来让我们一起来学习 Ubuntu命令大全 吧!祝你有所收获! 文章目录 前言&#x…...

单片机:实现教学上下课的自动打玲(附带源码)
单片机实现教学上下课的自动打铃 在学校或其他教育机构中,定时的打铃系统被广泛应用,用于提醒学生和老师上下课的时间。一个简单的自动打铃系统可以通过单片机实现,结合蜂鸣器和定时器控制,可以在设定的时间点自动打铃࿰…...

进程通信方式---共享映射区(无血缘关系用的)
5.共享映射区(无血缘关系用的) 文章目录 5.共享映射区(无血缘关系用的)1.概述2.mmap&&munmap函数3.mmap注意事项4.mmap实现进程通信父子进程练习 无血缘关系 5.mmap匿名映射区 1.概述 原理:共享映射区是将文件…...

深度学习实战智能交通计数
本文采用YOLOv8作为核心算法框架,结合PyQt5构建用户界面,使用Python3进行开发。YOLOv8以其高效的实时检测能力,在多个目标检测任务中展现出卓越性能。本研究针对车辆目标数据集进行训练和优化,该数据集包含丰富的车辆目标图像样本…...

【MySQL】MySQL表的操作
【MySQL】MySQL表的操作 🥕个人主页:开敲🍉 🔥所属专栏:MySQL🍋 🌼文章目录🌼 1. 创建表 2. 查看表结构 3. 修改表 4. 删除表 1. 创建表 create table table_name(表名称)( fiel…...

Redis篇-12--数据结构篇4--Hash内存模型(数组,链表,压缩列表zipList,哈希表,短结构)
Redis的Hash数据结构用于存储键值对(key-value形式)的集合(类似java中HashMap或对象)。为了在保证高效性能的同时节省内存,Redis对Hash的底层实现进行了多种优化。特别是通过使用压缩列表(ziplistÿ…...

二、windows环境下vscode使用wsl教程
本篇文件介绍了在windows系统使用vscode如何连接使用wsl,方便wsl在vscode进行开发。 1、插件安装 双击桌面vscode,按快捷键CtrlShiftX打开插件市场,搜索【WSL】点击安装即可。 2、开启WSL的linux子系统 点击左下方图标【Open a Remote Win…...

Qwen2-VL微调体验
1.配置环境 2.数据集准备 3.模型下载 4.注册SwanLab 5.微调 6.训练过程可视化 1.配置环境 本博客使用的是2B模型,所以仅用了单卡3090,若大一点的模型,自行根据实际情况准备显卡 安装Python>3.8 安装Qwen2-VL必要的库 pip install…...

论文的模拟环境和实验环境
模拟环境和实验环境 在撰写SCI计算机领域论文时,模拟环境和实验环境是两个重要的概念,它们之间存在显著的差异。 模拟环境主要是利用计算机、数学方法等手段对实际系统进行描述和分析的过程。在计算机科学中,模拟环境可以用于模拟各种算法、系统或网络的行为,以便在不需要…...

MySQL EXPLAIN 详解:一眼看懂查询计划
在日常的数据库开发中,我们经常需要分析 SQL 查询性能,而 EXPLAIN 是 MySQL 提供的利器,可以帮我们快速理解查询计划,优化慢查询。本文将详细解析 EXPLAIN 的输出字段及其含义,并结合实际案例分享优化思路。 一、什么是…...

自动呼入机器人如何与人工客服进行无缝切换?
自动呼入机器人如何与人工客服进行无缝切换? 原作者:开源呼叫中心FreeIPCC,其Github:https://github.com/lihaiya/freeipcc 自动呼入机器人与人工客服的无缝切换详解 自动呼入机器人与人工客服之间的无缝切换是确保客户体验连续…...
二分类模型的性能评价指标
1. 混淆矩阵 (Confusion Matrix) 预测正类预测负类实际正类 (P)True Positive (TP)False Negative (FN)实际负类 (N)False Positive (FP)True Negative (TN) True Positive (TP): 模型正确预测为正类的样本数。True Negative (TN): 模型正确预测为负类的样本数。False Positi…...

鸿蒙操作系统简介
华为鸿蒙系统(HUAWEI HarmonyOS),是华为公司于2019年8月9日在东莞举行的华为开发者大会(HDC.2019)上正式发布的面向全场景的分布式操作系统,可以创造一个超级虚拟终端互联的世界,将人、设备、场…...

单片机:实现蜂鸣器数码管的显示(附带源码)
单片机实现蜂鸣器数码管显示 蜂鸣器和数码管在嵌入式系统中广泛应用。蜂鸣器可以发出声音警告或提示,而数码管则用于显示数字或字母。在本项目中,我们将通过8051单片机实现一个控制蜂鸣器和数码管显示的系统,结合使用蜂鸣器和数码管…...

C语言期末复习笔记(上)
目录 一、为什么要学习C语言 1.C语言适合做什么 2.开发C程序的步骤 3.常用术语 二、C语言数据结构 1.常量与变量 (1)常量 编辑 (2)变量 2.数据类型 编辑 (1)数据类型的分类 (2&a…...

HarmonyOS 实时监听与获取 Wi-Fi 信息
文章目录 摘要项目功能概述代码模块详细说明创建 Wi-Fi 状态保存对象Wi-Fi 状态监听模块获取当前 Wi-Fi 信息整合主模块 运行效果展示性能分析总结 摘要 本文展示了如何使用 HarmonyOS 框架开发一个 Demo,用于监听手机的 Wi-Fi 状态变化并实时获取连接的 Wi-Fi 信息…...