Android HandlerThread、Looper、MessageQueue 源码分析
Android HandlerThread、Looper、MessageQueue 源码分析
简介
在 Android
开发中,大家应该对 HandlerThread
有一定了解。顾名思义,HandlerThread
是 Thread
的一个子类。与普通的 Thread
不同,Thread 通常一次只能执行一个后台任务,如果需要执行多个任务,就必须创建多个线程,这样容易导致资源管理复杂,甚至可能出现内存泄漏等问题。而 HandlerThread
有一个显著的特点,它能够串行地执行多个任务。每个任务通过消息的形式排队执行,线程池中的任务按顺序依次处理,无需手动管理线程的生命周期或调度过程。我们只需通过 Handler 将任务发送到 HandlerThread
中,它会自动按顺序执行,极大简化了开发过程。使用 HandlerThread
的最大优势是:它是单线程的,因此不需要担心线程安全问题。实际上,Android 源码中也有许多地方使用了 HandlerThread
,它在设计思想上值得我们学习和借鉴。接下来,我们将通过源码进一步了解它的实现原理。
源码分析
HandlerThread
内部持有一个Looper
,Looper
中持有一个消息队列MessageQueue
Looper
在了解HandlerThread
前,我们有必要先认识一下Looper
,Looper 是一个负责管理消息队列(MessageQueue
)并循环处理其中消息的类。简单来说,Looper 通过不断地从消息队列中取出消息并处理它们,来实现线程的消息处理机制。每个线程都有自己的消息队列和 Looper,通过 Looper,线程能够不断地处理任务直到任务队列为空。接下来我们会涉及到它以下几个核心接口。
prepare()
是一个静态方法,用来构建一个Looper
对象,quitAllowed
表示这个Looper是否支持退出,默认是支持,像Android 主线程的Looper
是不支持退出的
public static void prepare() {prepare(true);}
loopOnce
静态方法,顾名思义,循环一次,它的作用是从当前消息队列MessageQueue
中取一条符合条件的消息进行分发操作
private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {Message msg = me.mQueue.next(); // 取一条消息if (msg == null) {//正常没消息,队列会堵塞,不会返回null,所以这里是null肯定是已经要结束了,这里返回false 退出looperreturn false;}//分发消息代码逻辑省略}
loop()
静态方法,启动循环,在循环中不断调用loopOnce
来处理消息
//关键代码public static void loop() {final Looper me = myLooper();
//...for (;;) {if (!loopOnce(me, ident, thresholdOverride)) {//返回false 意味着循环结束return;}}}
quit()
和quitSafely()
退出looper,前者是立即退出,后者是处理完当前队列中所有消息后退出,最终是调用消息队列MessageQueue
对应的退出方法
public void quit() {mQueue.quit(false);
}public void quitSafely() {mQueue.quit(true);
}
HandlerThread
- 既然它是一个线程,那可以先从
run
方法入手:
@Overridepublic void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}
可以看出,线程跑起来后,先初始化了一个Looper
,然后启动死循环,HandlerThread
在这里充当的作用是在子线程中开启死循环接受和分发消息
这里有个地方比较有意思,在mLooper = Looper.myLooper();
后面,它调用了notifyAll()
,它起到了什么作用呢?
getLooper()
,我们应该知道要把任务提交给HandlerThread
执行,需要借助Handler
,但是Handler
的构造参数是需要传入一个Looper
对象,所以,这里对外公开了获取Looper的接口
public Looper getLooper() {if (!isAlive()) {return null;}boolean wasInterrupted = false;// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {wasInterrupted = true;}}}/** We may need to restore the thread's interrupted flag, because it may* have been cleared above since we eat InterruptedExceptions*/if (wasInterrupted) {Thread.currentThread().interrupt();}return mLooper;}
这里是直接返回了在run
中初始化的mLooper
,但是呢,在非当前线程获取mLooper
对象,就会引发线程安全问题,可能mLooper
还没被初始化就调用了getLooper()
,这样就有可能返回一个空的数据了,所以官方在这里做了while
循环,并且使用了wait()
堵塞,等待上面run
初始化完成后再notifyAll()
这里
getThreadHandler()
就是公开给外面向当前HandlerThread
插入消息的接口,内部维护着一个Handler
对象
@NonNullpublic Handler getThreadHandler() {if (mHandler == null) {mHandler = new Handler(getLooper());}return mHandler;}
quit()
、quitSafely()
同样,HandlerThread
也有退出的方法,其实现也是调用looper
对应的函数退出
MessageQueue
由于关于消息分发逻辑在其他地方讲过,这里只要分析退出队列的逻辑。
它实现了退出和安全退出的方法,这两个操作有什么区别呢,请看源码
next()
looper每调用一次loopOnce
,内部就会调用messageQueue获取一条消息
Message next() {//只放关键代码for (; ; ) {//堵塞,等待消息或者时机合适或主动唤醒nativePollOnce(ptr, nextPollTimeoutMillis);//...//符合分发条件的 返回这条消息给looperif (msg != null) {//...return msg;} else {// No more messages.nextPollTimeoutMillis = -1;}//...//退出状态 则释放队列,结束循环if (mQuitting) {dispose();return null;}}
}
quit()
实际逻辑由下面两个removeAllFutureMessagesLocked和removeAllMessagesLocked函数执行
void quit(boolean safe) {if (!mQuitAllowed) {throw new IllegalStateException("Main thread not allowed to quit.");}synchronized (this) {if (mQuitting) {//如果已经给队列设置了退出信号,下面的逻辑就不用走了return;}mQuitting = true;//标记当前队列处于退出状态,此时不再接受新的消息if (safe) {//安全退出removeAllFutureMessagesLocked();} else {//立即退出removeAllMessagesLocked();}//把队列的消息标记完成后,唤醒上面next的堵塞位置nativePollOnce,执行剩下的退出逻辑nativeWake(mPtr);}
}
//移除所有消息
private void removeAllMessagesLocked() {//mMessages 在这里是队列的头Message p = mMessages;while (p != null) {//从头开始,把所有消息标记为回收状态Message n = p.next;p.recycleUnchecked();p = n;}mMessages = null;//队头消息置空,next()执行时会判断这个,为空直接退出队列
}//移除所有未来消息,退出这一瞬间之前提交的消息保留继续执行
private void removeAllFutureMessagesLocked() {final long now = SystemClock.uptimeMillis();Message p = mMessages;if (p != null) {if (p.when > now) {//队头的时间比当前新,说明全是后面新加的,全部回收掉removeAllMessagesLocked();} else {//以下为从消息队列中找到退出那一瞬间时间一样的分割点,把分割点前的消息与队列断开链接Message n;for (;;) {n = p.next;if (n == null) {return;}if (n.when > now) {break;}p = n;}p.next = null;//断开链接,这里把队列从分割点切断do {p = n;n = p.next;p.recycleUnchecked();//把所有未来消息标记为回收状态} while (n != null);}}
}
从上面可以看出,安全退出时,队列会把所有未来消息移除掉,并且不再接受新的消息,队列中剩下的消息会继续被looper取,一直到取完为止,然后结束队列退出循环,而普通退出就会把队列中所有消息移除,然后紧接着结束队列退出循环
quit和quitSafely 应用场景有哪些呢
;//把所有未来消息标记为回收状态
} while (n != null);
}
}
}
从上面可以看出,安全退出时,队列会把所有未来消息移除掉,并且不再接受新的消息,队列中剩下的消息会继续被looper取,一直到取完为止,然后结束队列退出循环,而普通退出就会把队列中所有消息移除,然后紧接着结束队列退出循环### quit和quitSafely 应用场景有哪些呢
举一个简单的例子,在Android 使用Room操作数据库,由于操作数据库需要在子线程,所以,我们可以构造一个`HandlerThread`,专门处理操作数据库的任务,如果操作过程非常耗时,然后又要关闭数据库,
相关文章:
Android HandlerThread、Looper、MessageQueue 源码分析
Android HandlerThread、Looper、MessageQueue 源码分析 简介 在 Android 开发中,大家应该对 HandlerThread 有一定了解。顾名思义,HandlerThread 是 Thread 的一个子类。与普通的 Thread 不同,Thread 通常一次只能执行一个后台任务&#x…...
ASP.NET |日常开发中定时任务详解
ASP.NET |日常开发中定时任务详解 前言一、定时任务的概念与用途1.1 定义1.2 应用场景 二、在ASP.NET中实现定时任务的方式2.1 使用System.Timers.Timer2.2 使用Quartz.NET 三、定时任务的部署与管理3.1 部署考虑因素3.2 管理与监控 结束语优质源码分享 ASP.NET &am…...
Unity动态读取外部图片转Texture2D,内存过大问题解决方案
问题描述 加载原始图片2.63M的图片,分辨率为3023*4032,占用内存108.5M 加载原始图片12.6 M的图片,分辨率为6000*8000,占用内存427.2M 太恐怖了吧 解决方案 1.加载完图片,等比缩放,宽高改为1024或者512以下 1024占用5.2M,512占用1.3M,相比小了很多 2.原始Texture2…...
【Where语法全解密】.NET开源ORM框架 SqlSugar 系列
文章目录 前言一、普通表达式查询 Where二、根据SQL查询 where三、动态查询四、动态拼表达式查询 ,拼拉姆达五、条件拼接查询六、链式条件查询七、根据Class查询,根据实查询八、根据Class中主键查询九、根据字典查询十、查询函数十一、Where中有子查询十…...
人工智能练习题--大题题库速通指南
随着人工智能(AI)技术的快速发展,越来越多的企业和个人开始重视AI的应用和发展。在这个背景下,人工智能训练师这一职业应运而生,他们专注于数据标注、模型训练、算法优化等方面的工作,以确保AI系统能够准确…...
写给Pythoner的前端进阶指南(四):异步编程 - 原生支持的 async/await
在开发中,异步编程是处理并发任务的重要方式。无论是请求数据、读取文件还是执行网络操作,异步编程能够有效提升程序的性能和响应速度。Python 和 JavaScript 都支持异步编程,但它们的实现方式有所不同。在这篇文章中,我们将深入探…...
项目搭建+姓名唯一性校验
一 : 添加时对姓名进行唯一性校验 ① : 给姓名绑定一个改变事件 ② : 取值 ③ : 组装对象 ④ : ajax (Controller定义对姓名唯一性校验的方法) 在成功回调函数里 判断姓名是否添加过(0>已添加 1>未添加) 未添加过,添加成功后,(清空) /*** 对姓名进行唯一性校验*/$("…...
MacOS下PostIn安装配置指南
PostIn是一款开源免费的接口管理工具, 下面介绍私有部署版本的MacOS下安装与配置。私有部署版本更适合有严格数据安全要求的企业,实现对数据和系统的完全控制。   1、MacOS服务端安装 Mac安装包下载地址:下载Mac安…...
时光回响,中原之声 ——漓岛⾳乐节x SONICE
音乐像呼吸一样在大地起伏,中原的回响透过时空,从遥远的内陆弥漫扩散至温暖的南方,年轻的呼唤似一阵快雨淋落在辽阔的海面,波澜游弋,终掀浪涛。来自中原的独立音乐厂牌SONICE将与澄迈漓岛音乐节联合呈现乘风舞台&#…...
获取apk信息(包名,版本等) aapt dump badging (apk文件路径)
1.找到sdk路径下aapt文件: Android\Sdk\build-tools\29.0.0 (29.0.0是构建版本,可以是其他版本如27.0.3) 2.window dos窗口cd 到这个路径下:Android\Sdk\build-tools\29.0.0 ,也可以配置到 环境变量 3.执行 aapt dump badging (apk文件路径…...
C++ 23版的最新特性
C 23 是 C 编程语言的最新标准版本,于 2023 年正式发布,带来了诸多新特性与改进,以下是一些主要内容: 1.语言特性 1.显式对象参数(Deducing this):显式对象参数(Deducing this&…...
【微信小程序开发 - 3】:项目组成介绍
文章目录 项目组成介绍项目的基本组成结构小程序页面的组成部分JSON配置文件的作用app.json文件project.config.json文件sitemap.json文件页面的 .json 配置文件新建小程序页面修改项目首页 XWML模板XWML 和 HTML 的区别 WXSS样式WXSS 和 CSS 的区别 .js文件 项目组成介绍 项目…...
分布式超低耦合,事件驱动架构(EDA)深度解析
目录 引言什么是事件驱动架构(EDA)事件驱动架构的基本原理事件驱动架构的特点与优势事件驱动架构与分布式系统事件驱动架构的关键组件事件驱动架构的实施步骤常见的事件驱动架构模式事件驱动架构在分布式系统中的应用场景挑战与解决方案总结 引言 在当…...
深入理解 Linux wc 命令
文章目录 深入理解 Linux wc 命令1. 基本功能2. 常用选项3. 示例3.1 统计文件的行、单词和字符数3.2 仅统计行数3.3 统计多个文件的总和3.4 使用管道统计命令输出的行数 4. 实用案例4.1 日志分析4.2 快速统计代码行数4.3 统计单词频率 5. 注意事项6. 总结 深入理解 Linux wc 命…...
轻松拿捏Spring
目录 Spring基础 什么是Spring框架 Spring 包含的模块有哪些? Core Container AOP Data Access/Integration Spring Web Messaging Spring Test Spring,Spring MVC,Spring Boot 之间什么关系? Spring基础 什么是Spring框架 Spring 是一款开源的轻量级 Java 开发框…...
使用 `du` 命令可以查看根目录下每个子目录占用的磁盘空间大小
使用 du 命令可以查看根目录下每个子目录占用的磁盘空间大小。 查看根目录下子目录大小的命令 sudo du -sh /*解释: du:显示文件和目录的磁盘使用情况。-s:只显示每个目录的总大小(不递归显示子目录)。-h࿱…...
Python练习之列表的使用
(搭配主页知识点) 【练习要求】 针对知识点列表定义、追加、列表元素读取、查找安排的本实例。要求实现:有一个列表,内容是:[21,25,21,23,22,20],记录的是一批学生的年龄请通过列表的功能(方法),对其进行…...
深入理解 HTTP HEAD 请求:节省带宽、提高效率的秘密武器
序言: 在HTTP协议中,HEAD请求是一种非常实用且被忽略的请求方法。与GET请求相似,HEAD请求同样从服务器获取资源,但与GET请求的最大不同之处在与,HEAD请求 仅返回响应的头部信息,不包含内容提。这使得HEAD请…...
电商数据流通的未来:API接口的智能化与自动化趋势
在数字化时代,电子商务行业正在以前所未有的速度发展,而API(应用程序编程接口)接口作为电商领域的重要组成部分,其应用和发展趋势也日益受到关注。API接口作为电商系统与外部服务或平台交互的桥梁,对电商数…...
[python]使用flask-caching缓存数据
简介 Flask-Caching 是 Flask 的一个扩展,为任何 Flask 应用程序添加了对各种后端的缓存支持。它基于 cachelib 运行,并通过统一的 API 支持 werkzeug 的所有原始缓存后端。开发者还可以通过继承 flask_caching.backends.base.BaseCache 类来开发自己的…...
13.罗意文面试
1、工程化与架构设计(考察项目管理和架构能力) 1.1 你负责的可视化编排项目中,如何设计组件的数据结构来支持"拖拉拽"功能?如何处理组件间的联动关系? // 组件数据结构示例 {components: [{id: comp1,type…...
有监督学习 vs 无监督学习:机器学习的两大支柱
有监督学习 vs 无监督学习:机器学习的两大支柱 有监督学习 vs 无监督学习:机器学习的两大支柱一、有无“老师”来指导二、解决的问题类型不同三、模型的输出不同 有监督学习 vs 无监督学习:机器学习的两大支柱 在机器学习的奇妙世界里&#…...
创建第一个QML项目
文章目录 使用 Qt Creator 创建 Qt Quick 项目详解为什么选择 Qt Creator?1. 打开 Qt Creator2. 选择项目模板3. 设置项目名称与路径4. 定义项目细节5. 配置构建套件6. 检查项目配置7. 编译并运行项目后续操作修改界面添加功能 总结 使用 Qt Creator 创建 Qt Quick …...
【k8s集群应用】K8S二进制安装大致步骤(简略版)
文章目录 K8S二进制安装部署etcd测试etcd集群(可选)恢复etcd数据库 部署master组件部署node组件K8S kubeadm安装关键命令更新kubeadm安装的K8S证书有效期方法一方法二查看证书有效期 K8S二进制安装 部署etcd 使用cfssl工具签发证书和私钥下载解压etcd软…...
cudnn版本gpu架构
nvcc --help 可以看 --gpu-architecture 写到的支持的架构 NVIDIA 的 GPU 架构是按代次发布的,以下是这些架构的对应说明: NVIDIA Hopper: 这是 NVIDIA 于 2022 年推出的架构之一,面向高性能计算(HPC)和人工智能&…...
智能体实战(需求分析助手)一、需求概述及迭代规划
需求分析助手开发迭代规划 功能概述 需求分析助手是一款基于大模型的智能系统,旨在帮助用户高效完成需求获取、需求分析、需求文档编写及需求验证的全流程工作。通过对用户输入的智能处理和分析,需求分析助手能够简化需求管理流程,并根据不同业务场景提供定制化支持。 核心…...
二叉搜索树Ⅲ【东北大学oj数据结构8-3】C++
二叉搜索树 III B:在二叉搜索树II中加入delete指令,创建程序对二叉搜索树T执行如下指令。 插入 k:将key k 插入到 T 中。 find k:报告T中是否存在key k。 delete k:删除key为 k 的节点。 打印:使用中序树遍…...
基于ceres优化的3d激光雷达开源算法
以下是一些基于CERES优化的开源激光雷达SLAM或相关算法: (1) LOAM (Lidar Odometry And Mapping) 简介: LOAM是一种经典的激光雷达里程计和建图算法,它通过提取特征点(角点和平面点),利用ICP(Iterative Cl…...
2023.9 Explainability for Large Language Models: A Survey
问题 可解释性问题:大语言模型(LLMs)内部机制不透明,难以理解其决策过程,如在自然语言处理任务中,不清楚模型如何根据输入生成特定的预测结果。模型评估问题:缺乏有效的评估指标和方法来衡量解…...
集成方案 | Docusign + 金蝶云,实现合同签署流程自动化!
本文将详细介绍 Docusign 与金蝶云的集成步骤及其效果,并通过实际应用场景来展示 Docusign 的强大集成能力,以证明 Docusign 集成功能的高效性和实用性。 在当今商业环境中,流程的无缝整合与数据的实时性对于企业的成功至关重要。金蝶云&…...
[LeetCode-Python版] 定长滑动窗口3——1461. 检查一个字符串是否包含所有长度为 K 的二进制子串
题目 给你一个二进制字符串 s 和一个整数 k 。如果所有长度为 k 的二进制字符串都是 s 的子串,请返回 true ,否则请返回 false 。 示例 1: 输入:s “00110110”, k 2 输出:true 解释:长度为 2 的二进制…...
简单工厂模式和策略模式的异同
文章目录 简单工厂模式和策略模式的异同相同点:不同点:目的:结构: C 代码示例简单工厂模式示例(以创建图形对象为例)策略模式示例(以计算价格折扣策略为例)UML区别 简单工厂模式和策…...
Docker容器五种网络驱动模式详解
Docker 网络用于在容器之间以及容器与外部网络之间提供通信功能。它允许容器在隔离的网络环境中运行,同时也能根据需要与其他容器或外部网络进行交互。通过使用网络驱动,Docker 可以创建不同类型的网络,以满足各种应用场景的需求。 传统上&am…...
从客户跟进到库存管理:看板工具赋能新能源汽车销售
在新能源汽车市场日益扩张的今天,门店销售管理变得更加复杂和重要。从跟踪客户线索到优化订单流程,再到团队协作,效率低下常常成为许多门店的“隐形成本”。如果你曾为销售流程不畅、客户管理混乱而苦恼,那么一种简单直观的工具—…...
汽车IVI中控开发入门及进阶(41):视频播放器MPlayer
版本: MPlayer 1.5 2022年已发布。 MPlayer 1.5与最新FFmpeg版本(5.0)和当前FFmpeg开发版本(FFmpeg master)兼容。tarball已经包含一个FFmpeg快照,因此不需要单独获取它。如果想遵循MPlayer和FFmpeg的最新改进,强烈建议你使用开发版本。 MPlayer - The Movie Playerht…...
Odoo:免费开源ERP的AI技术赋能出海企业电子商务应用介绍
概述 伴随电子商务的持续演进,客户对于便利性、速度以及个性化服务的期许急剧攀升。企业务必要探寻创新之途径,以强化自身运营,并优化购物体验。达成此目标的最为行之有效的方式之一,便是将 AI 呼叫助手融入您的电子商务平台。我们…...
看板工具助力餐饮与酒店行业实现数字化转型,提升管理与运营效率
在餐饮与酒店行业,服务质量和客户体验是衡量企业成功的关键因素。随着客户需求的不断多样化以及市场竞争的加剧,传统的管理模式逐渐难以满足高效运营的需求。尤其在高峰期,如何优化内部流程、提高服务效率和响应速度,成为了许多餐…...
网络安全(3)_安全套接字层SSL
4. 安全套接字层 4.1 安全套接字层(SSL)和传输层安全(TLS) (1)SSL/TLS提供的安全服务 ①SSL服务器鉴别,允许用户证实服务器的身份。支持SSL的客户端通过验证来自服务器的证书,来鉴别…...
国标GB28181网页直播平台EasyGBS:网络摄像机中的音频及音频编码技术解析
在网络摄像机领域,音频质量及其编码方式对于视频监控系统的整体性能至关重要。音频作为视频监控系统的重要组成部分,不仅能够提供现场的声音信息,增强监控的实时性和准确性,还能在事件发生后为调查提供宝贵的语音证据。 一、网络摄…...
为何VisualRules更适合技术人员使用
什么是规则引擎 规则引擎是一种软件组件,它允许将业务规则从应用程序的核心代码中分离出来,以一种更加灵活、易于管理和维护的方式来定义、存储和执行这些规则。简单来说,它就像是一个专门处理规则的 “大脑”,可以根据预先设定的…...
PyTorch 2.0 以下版本中设置默认使用 GPU 的方法
PyTorch 2.0 以下版本中设置默认使用 GPU 的方法 在 PyTorch 2.0以下版本中,默认情况下仍然是使用 CPU 进行计算,除非明确指定使用 GPU。在 PyTorch 2.0 以下版本中,虽然没有 torch.set_default_device 的便捷方法,但可以通过显式…...
Redis篇-19--运维篇1-主从复制(主从复制,读写分离,配置实现,实战案例)
1、概述 Redis的主从复制(Master-Slave Replication)是一种数据冗余机制,它允许将一台Redis服务器的数据复制到其他Redis服务器。在主从复制中,有一台主服务器(Master)和一个或多个从服务器(Sl…...
springboot449教学资源共享平台(论文+源码)_kaic
摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统教学资源共享平台信息管理难度大,容错率低&am…...
Unbuntu下怎么生成SSL自签证书?
环境: WSL2 Unbuntu 22.04 问题描述: Unbuntu下怎么生成SSL自签证书? 解决方案: 生成自签名SSL证书可以使用OpenSSL工具,这是一个广泛使用的命令行工具,用于创建和管理SSL/TLS证书。以下是生成自签名…...
Ubuntu18.04——换源
一、前提说明 系统自带的源往往下载很慢,通过换源操作后,往往下载/更新 速度大幅提升 每种版本对应的不一样,例如Ubuntu18.04和Ubuntu20.04的有差异,所以换源需要根据不同版本对应的命令 二、操作步骤 0.备份原先的 /etc/apt/sou…...
crictl和ctr与docker的命令的对比
crictl是遵循CRI接口规范的一个命令行工具,通常用它来检查和管理kubelet节点上的容器运行时和镜像 ctr是containerd的一个客户端工具, 接下来就是crictl的的常见命令,其中能完全替代docker命令的参照下列表格 操作crictldocker查看运行容器…...
Java 技术面试常见问题解析
1.说说Mybatis的缓存机制: MyBatis 是一个优秀的持久层框架,它简化了企业应用开发中数据库操作的代码。MyBatis 提供了一级缓存和二级缓存机制来优化对数据库的访问。 一级缓存 (SqlSession级别的缓存) 一级缓存是 MyBatis 中默认开启且无法关闭的缓存机制。它存…...
数据结构,链表的简单使用
任意位置删除: void Any_Del(LinkListPtr h,int a)//任意删 {if(NULLh||a>h->len){printf("删除失败");}LinkListPtr ph;for(int i0;i<a-1;i){pp->next;}LinkListPtr p2p;p2p2->next;p->nextp->next->next;free(p2);p2NULL;h-&g…...
go引用包生成不了vendor的问题
比如我要引入github.com/jinzhu/gorm这个包. 1. 首先获取包 go get github.com/jinzhu/gorm 这时go.mod文件中也有这个包依赖信息了. 2. 然后构建vendor go mod vendor 结果发现vendor目录下没有生成对应的包, 而且modules.txt也注释掉这个包了. 原因是没有其进行引用, go…...
C语言——实现求出最大值
问题描述:利用C语言自定义函数求出一维数组里边最大的数字 //利用函数找最大数#include<stdio.h>int search(int s[9]) //查找函数 {int i , max s[0] , max_xia 0;for(i0;i<9;i){if(s[i] > max){max_xia i;max s[max_xia];}}return max; } in…...