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

【Linux C | 多线程编程】线程的连接、分离,资源销毁情况

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-04-01 14:52:46

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
    • ✨1.1
    • ✨1.2
  • 🎄二、线程的连接 pthread_join
  • 🎄三、线程的分离 pthread_detach
  • 🎄四、必须连接线程 或 分离线程
  • 🎄五、总结


在这里插入图片描述

🎄一、概述

记住一句话,“创建线程后,要么连接该线程,要么使该线程分离,否则可能导致资源无法释放”。

怎样连接一个线程,连接线程是注意什么?
为什么要连接线程?
怎样分离一个线程,分离线程是注意什么?

本文将围绕线程的连接、分离操作去召开,让读者可以清楚上面几个问题的答案。


✨1.1

✨1.2

在这里插入图片描述

🎄二、线程的连接 pthread_join

线程连接(joining):使用 pthread_join 函数,用来等待某线程的退出并接收它的返回值。

线程连接可以用来等待一个线程执行,也可以用来获取线程的返回值,也可以即等待线程的结束又获取线程的返回值。这个等待有点类似于进程等待子进程退出的wait操作,但是有两点区别:

  • 1、进程间的等待只能是父进程等待子进程,而线程则没有这样的说法,只要是在一个线程组(进程)内,就可以对另外一个线程执行连接(join)操作;
  • 2、进程可以等待任一子进程的退出。而线程没有这样的操作,需要明确指定要连接的线程ID。这样的设计可以避免库函数的线程被连接了而导致库函数无法连接自己的线程。

pthread_join函数原型:

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
  • 函数描述:pthread_join可以等待某线程的退出并接收它的返回值。根据等待的线程是否退出, 可得到如下两种情况:
    • 等待的线程尚未退出, 那么pthread_join的调用线程就会陷入阻塞。
    • 等待的线程已经退出, 那么pthread_join函数会将线程的退出值(void*类型) 存放到retval指针指向的位置。
  • 函数参数:
    • thread:传入参数,要连接的线程ID;
    • retval:传出参数,用来接收线程返回值。
  • 函数返回值:成功返回 0,调用失败,和pthread_create函数一样, errno作为返回值返回。
    • EDEADLK:死锁,如自己连接自己,或者A连接B,B又连接A;
    • EINVAL:线程不是一个可连接(joinable)的线程;
    • EINVAL:已经有其他线程捷足先登,连接目标线程;
    • ESRCH:传入的线程ID不存在,查无此线程。

可能产生连接死锁的两个情况:
1、线程A连接线程A(自己连接自己);
2、线程A连接线程B,线程B连接线程A。
在这里插入图片描述

🌰举例子:

// 07_pthread_join.c
// gcc 07_pthread_join.c -l pthread
#include <stdio.h>
#include <pthread.h>int ret = -1;		// 全局变量记录线程返回值
void *func(void *arg)
{int *parg = arg;printf("this thread arg is %d, my threadID is %lx \n", *parg, (unsigned long)pthread_self());ret=*parg+1;return (void*)&ret;
}
int main()
{int arg=10;pthread_t threadId;pthread_create(&threadId, NULL, func, &arg);int *pRet = NULL;pthread_join(threadId, (void**)&pRet);printf("pthread_join end, ret=%d\n", *pRet);return 0;
}

在这里插入图片描述

🎄三、线程的分离 pthread_detach

默认情况下, 新创建的线程处于可连接(Joinable)的状态, 可连接状态的线程退出后, 需要对其执行连接操作, 否则线程资源无法释放,从而造成资源泄漏。

有时,我们并不关心该线程的返回值,也不想阻塞等待,但不执行连接又会资源泄露,那怎么办?线程库考虑到这种使用场景,提供了 pthread_detach 函数可将线程设置成已分离(detached)状态。处于已分离(detached)状态的线程退出时,由系统负责回收该线程的资源。

pthread_detach函数原型:

#include <pthread.h>
int pthread_detach(pthread_t thread);
Compile and link with -pthread.
  • 函数描述:pthread_detach函数将thread指定的线程标记为已分离。当一个分离的线程终止时,它的资源会自动释放回系统,而不需要另一个线程与终止的线程连接。试图分离已分离的线程会导致未指定的行为。
  • 函数参数:thread,要分离的线程ID。
  • 函数返回值:成功返回 0,调用失败,和pthread_create函数一样, errno作为返回值返回。
    • EINVAL:线程不是一个可连接(joinable)的线程,已经处于已分离状态;
    • ESRCH:传入的线程ID不存在,查无此线程。

其他相关内容:

  • 1、将线程的属性设定为已分离的第2种方式,使用 pthread_attr_setdetachstate 函数,这个比较少用可以了解一下。
    #include <pthread.h>
    int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
    int pthread_attr_getdetachstate(pthread_attr_t *attr,int *detachstate);
    
  • 2、线程分离可由其他线程对其执行分离,也可以线程自己执行pthread_detach函数, 将自身设置成已分离的状态:
    pthread_detach(pthread_self());
    
  • 3、所谓已分离, 并不是指线程失去控制, 不归线程组管理, 而是指线程退出后, 系统会自动释放线程资源。

🌰举例子:下面例子是主线程执行分离的,更常见的是,线程内部自己执行分离。

// 07_pthread_detach.c
// gcc 07_pthread_detach.c -l pthread
#include <stdio.h>
#include <pthread.h>
void *func(void *arg)
{int *parg = arg;printf("this thread arg is %d, my threadID is %lx \n", *parg, (unsigned long)pthread_self());return NULL;
}
int main()
{int arg=10;pthread_t threadId;pthread_create(&threadId, NULL, func, &arg);pthread_detach(threadId);	// 对该线程执行分离printf("pthread_detach exec\n");while(1); // 保持主线程不退出return 0;
}

在这里插入图片描述

🎄四、必须连接线程 或 分离线程

这一小节,我们了解为什么必须要 连接线程分离线程 ?
因为既不分离线程又不连接已经退出的线程,可能会导致资源无法释放。

注意已连接已分离 的线程退出时,该线程的资源并没有立即调用munmap来释放掉,而是保留着被后面新建的线程复用。NPTL线程库的设计。
释放线程资源的时候,NPTL认为进程可能再次创建线程,而频繁地munmap和mmap会影响性能,所以NTPL将该栈缓存起来,放到一个链表之中,如果有新的创建线程的请求,NPTL会首先在栈缓存链表中寻找空间合适的栈,有的话,直接将该栈分配给新创建的线程。

下面通过一个例子来看看线程退出后的资源情况。

// 07_pthread_join_detach_test.c
// gcc 07_pthread_join_detach_test.c -l pthread -DNO_JOIN_DETACH	// 没有连接、分离
// gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_JOIN		// 使用线程的连接
// gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_DETACH		// 使用线程的分离
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
void *func(void *arg)
{
#ifdef THREAD_DETACHpthread_detach(pthread_self());
#endifprintf("threadID = %lx, TID=%u \n", (unsigned long)pthread_self(), syscall(SYS_gettid));// 获取线程属性pthread_attr_t gattr;int s = pthread_getattr_np(pthread_self(), &gattr);if (s != 0){printf("pthread_getattr_np error\n");return NULL;}// 获取线程栈地址和大小void *stkaddr;size_t v;s = pthread_attr_getstack(&gattr, &stkaddr, &v);if (s != 0){printf("pthread_attr_getstackaddr error\n");return NULL;}printf("Stack address = %p, size=%luk btye\n", stkaddr, v/1024);sleep(3);printf("TID=%u EXIT\n", syscall(SYS_gettid));return NULL;
}
int main()
{
#ifdef NO_JOIN_DETACHprintf("NO_JOIN_DETACH PID=%u\n",syscall(SYS_gettid));
#endif
#ifdef THREAD_JOINprintf("THREAD_JOIN PID=%u\n",syscall(SYS_gettid));
#endif
#ifdef THREAD_DETACHprintf("THREAD_DETACH PID=%u\n",syscall(SYS_gettid));
#endif// 1、创建第一个线程pthread_t threadId, threadId2;pthread_create(&threadId, NULL, func, NULL);// 2、等待上个线程结束
#ifdef THREAD_JOINpthread_join(threadId, NULL);
#else // 没有等待线程,就使用sleep等待上个线程结束sleep(5);
#endif// 3、创建第二个线程pthread_create(&threadId2, NULL, func, NULL);pause(); // 保持主线程不退出return 0;
}

下面代码演示了线程资源使用的三个情况,下面分别看看其运行结果:

  • 🌰1、没有连接、分离;
    复制上面代码,运行gcc 07_pthread_join_detach_test.c -l pthread -DNO_JOIN_DETACH编译,可以看到各个线程的栈地址不一样:
    在这里插入图片描述
    运行pmap查看内存分布情况,也可以看到这两个地址,如下图:
    在这里插入图片描述
    可以得出一个结论:如果线程既不连接、又不分离的话,那么:
    1) 已经退出的线程,其空间没有被释放,仍然在进程的地址空间之内。
    2) 新创建的线程,没有复用刚才退出的线程的地址空间。

  • 🌰2、使用线程的连接;
    接下来看看,使用了线程连接的情况,上面代码运行gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_JOIN编译,运行结果如下,可以看到两个线程的栈地址是一样的,也就是说,第一个线程退出后,其地址空间被后面新建的线程复用
    在这里插入图片描述
    运行pmap查看进程内存分布情况,只有一个线程栈地址,如下图:
    在这里插入图片描述

  • 🌰3、使用线程的分离。
    接下来看看,使用了线程分离的情况,上面代码运行gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_DETACH编译,运行结果如下,可以看到两个线程的栈地址是一样的,也就是说,第一个线程退出后,其地址空间被后面新建的线程复用
    在这里插入图片描述
    运行pmap查看进程内存分布情况,只有一个线程栈地址,如下图:
    在这里插入图片描述


在这里插入图片描述

🎄五、总结

本文结束了Linux系统编程的线程的连接(pthread_join)、线程的分离(pthread_detach),以及介绍了为什么要使用线程的连接、分离。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
《Linux环境编程:从应用到内核》

相关文章:

【Linux C | 多线程编程】线程的连接、分离,资源销毁情况

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-04-01 1…...

kubernetes-Pod基于污点、容忍度、亲和性的多种调度策略(二)

Pod调度策略 一.污点-Taint二.容忍度-Tolerations三.Pod常见状态和重启策略1.Pod常见状态2.Pod的重启策略2.1测试Always重启策略2.2测试Never重启策略2.3测试OnFailure重启策略&#xff08;生产环境中常用&#xff09; 一.污点-Taint 在 Kubernetes 中&#xff0c;污点&#x…...

数码管时钟--LABVIEW编程

一、程序的前面板 1.获取系统时钟&#xff0c;年月日&#xff0c;时分秒&#xff0c;用14个数码管显示。 2.闹钟设定小时和分钟。 二、程序的后面板 三、程序运行图 四、程序源码 源程序可以在百度网盘自行下载&#xff0c;地址链接见下方。 链接&#xff1a;https://pan.b…...

linux安装指定版本docker

目录 查看主机上docker版本 配置docker的yum源 安装指定版本docker-20.10.14 查看yum中docker的版本 此命令装完后&#xff0c;任然会是最新版本的docker 卸载已安装docker 安装docker docker依赖包有冲突 解决冲突报错 再次执行安装docker命令 查看主机上docker版本 …...

C++刷题篇——05静态扫描

一、题目 二、解题思路 注意&#xff1a;注意理解题目&#xff0c;缓存的前提是先扫描一次 1、使用两个map&#xff0c;两个map的key相同&#xff0c;map1&#xff1a;key为文件标识&#xff0c;value为文件出现的次数&#xff1b;map2&#xff1a;key为文件标识&#xff0c;va…...

Unity AI Navigation自动寻路

目录 前言一、Unity中AI Navigation是什么&#xff1f;二、使用步骤1.安装AI Navigation2.创建模型和材质3.编写向目标移动的脚本4.NavMeshLink桥接组件5.NavMeshObstacle组件6.NavMeshModifler组件 三、效果总结 前言 Unity是一款强大的游戏开发引擎&#xff0c;而人工智能&a…...

HarmonyOS实战开发-如何实现一个简单的健康生活应用(上)

介绍 本篇Codelab介绍了如何实现一个简单的健康生活应用&#xff0c;主要功能包括&#xff1a; 用户可以创建最多6个健康生活任务&#xff08;早起&#xff0c;喝水&#xff0c;吃苹果&#xff0c;每日微笑&#xff0c;刷牙&#xff0c;早睡&#xff09;&#xff0c;并设置任…...

React中使用antDesign框架

1.在React项目中使用Ant Design&#xff0c;首先需要安装Ant Design: npm install antd --save 2.按需引入Ant Design组件&#xff0c;以减小最终打包的大小。使用babel-plugin-import插件可以实现按需加载。首先安装插件&#xff1a; npm install babel-plugin-import --save-…...

Electron安全防护实战:应对常见安全问题及权限控制措施

Electron安全防护实战&#xff1a;应对常见安全问题及权限控制措施 引言常见安全问题及其危害提升 Electron 应用安全性的措施限制渲染进程权限防止XSS与内容注入加固应用更新流程严格管理硬件权限使用安全的第三方模块加密敏感数据存储实现进程间通信&#xff08;IPC&#xff…...

StringBuffer与StringBuilder

1.区别 (1). String : 不可变字符序列. (2). StringBuffer : 可变字符序列.线程安全&#xff0c;但效率低. (3). StringBuilder : 可变字符序列.线程不安全&#xff0c;但效率高. 既然StringBuffer与StringBuilder都是可变字符序列&#xff0c;但二者咋区分开呢&#xff1f…...

HCIP综合实验拓扑

实验要求 1.R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有I地址; 2、R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方: R2与R5之间使用ppp的CHAP认证&#xff0c;R5为主认证方; R3与R5之间使用HDLC封装; 3R1、R2、R3构建一个MGRE环境&#xf…...

nuxt学习

一、遇到的问题 1、nuxt初始化失败问题解决方案 使用npm和pnpm初始化都失败 原因&#xff1a;主机连不上DNS服务器 解决方案 Step1: 打开文件夹 Windows:路径&#xff1a;C:\Windows\System32\drivers\etc Mac: 路径&#xff1a;/etc/hosts Step2: 使用记事本方式打开 …...

VS学习建议

Visual Studio&#xff08;简称VS&#xff09;是由微软公司开发的一款集成开发环境&#xff08;IDE&#xff09;&#xff0c;支持多种编程语言&#xff0c;主要用于Windows平台上的应用程序开发。学习使用Visual Studio涉及多个方面&#xff0c;以下是一些关键的学习内容&#…...

java汇总区间

给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说&#xff0c;nums 的每个元素都恰好被某个区间范围所覆盖&#xff0c;并且不存在属于某个范围但不属于 nums 的数字 x 。 列表中的每个区间范围 [a,b] 应该按…...

【笔记】OpenHarmony设备开发:搭建开发环境(Ubuntu 20.04,VirtualBox 7.0.14)

参考&#xff1a;搭建开发环境&#xff08;HarmonyOS Device&#xff09; Note&#xff1a;Windows系统虚拟机中Ubuntu系统安装完成后&#xff0c;根据指导完成Ubuntu20.04基础环境配置&#xff08;HarmonyOS Connect 开发工具系列课&#xff09; 系统要求 Windows系统要求&…...

计算机视觉新巅峰,微软牛津联合提出MVSplat登顶3D重建

开篇&#xff1a;探索稀疏多视图图像的3D场景重建与新视角合成的挑战 3D场景重建和新视角合成是计算机视觉领域的一项基础挑战&#xff0c;尤其是当输入图像非常稀疏&#xff08;例如&#xff0c;只有两张&#xff09;时。尽管利用神经场景表示&#xff0c;例如场景表示网络&a…...

halcon图像腐蚀

1、原理 使用结构元素在图像上移动&#xff0c;只有结构元素上的所有像素点都属于图像中时&#xff0c;才保留结构元素中心点所在的像素&#xff0c;常用于分离连接的两个物体、消除噪声。 2、halcon代码 dev_open_file_dialog (read_image, default, default, Selection) r…...

neo4j使用详解(六、cypher即时时间函数语法——最全参考)

Neo4j系列导航&#xff1a; neo4j及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 6.时间函数-即时类型 表示具体的时刻的时间类型函数 6.1.date函数 年-月-日时间函数&#xff1a; yyyy-mm-dd 6.1.1.获取date da…...

Web 前端性能优化之一:性能模型及网页原理

一、RAIL 性能模型 RAIL性能模型指出了用户对不同延迟时间的感知度&#xff0c;以用户为中心的原则&#xff0c;就是要让用户满意网站或应用的性能体验。 RAIL &#xff1a;响应(Response)、动画(Animation)、空闲(Idle)、加载(Load) RAIL 性能模型 用户感知延迟的时间窗口 1…...

常用的主流好用的WEB自动化测试工具强烈推荐

在业务使用的自动化测试工具很多。有开源的&#xff0c;有商业化的&#xff0c;各有各得特色&#xff0c;各有各得优点&#xff01;下面我就介绍几个我用过的一款非常优秀的国产自动化测试工具。在现有的自动化软件当中&#xff0c;都是以元素的name、id、xpath、class、tag、l…...

分享几个非常不错嵌入式开源项目,一定不要错过

大家好&#xff0c;我是知微&#xff01; 经常有小伙伴后台私信我&#xff1a; 有没有好的开源项目推荐怎么样才能提升自己的编程能力 那么这篇文章就推荐几个还不错的开源项目&#xff0c;感兴趣的小伙伴可以学习一下&#xff01; 日志库EasyLogger https://github.com/ar…...

Golang基础-4

Go语言基础 介绍 基础 数组(array) 数组声明 元素访问与修改 数组遍历 关系运算 切片创建 多维数组 介绍 本文介绍Go语言中数组(array)操作(数组声明、元素访问与修改、数组遍历、关系运算、切片创建、多维数组)等相关知识。 基础 数组 数组是具有相同数据类型的…...

2024软件设计师备考讲义——UML(统一建模语言)

UML的概念 用例图的概念 包含 <<include>>扩展<<exted>>泛化 用例图&#xff08;也可称用例建模&#xff09;描述的是外部执行者&#xff08;Actor&#xff09;所理解的系统功能。用例图用于需求分析阶段&#xff0c;它的建立是系统开发者和用户反复…...

HTML——1.简介、基础、元素

一、简介 HTML&#xff08;HyperText Markup Language&#xff09;是一种用于创建网页的标记语言。它使用标记&#xff08;tag&#xff09;来描述网页的结构和内容。HTML被用于定义网页中的文本、图像、链接、多媒体以及其他元素的排列和呈现方式。 HTML文档是由一系列的HTML…...

Rust 标准库:std::env::args() 函数简介

std::env::args() 是 Rust 标准库中的一个函数&#xff0c;它属于 std::env 模块。这个函数用于获取并返回一个迭代器&#xff0c;该迭代器包含了程序运行时从命令行传入的所有参数。 当你运行一个 Rust 程序并从命令行传递参数时&#xff0c;例如&#xff1a; my_rust_progr…...

【Blockchain】GameFi | NFT

Blockchain GameFiGameFi顶级项目TheSandbox&#xff1a;Decentraland&#xff1a;Axie Infinity&#xff1a; NFTNFT是如何工作的同质化和非同质化区块链协议NFT铸币 GameFi GameFi是游戏和金融的组合&#xff0c;它涉及区块链游戏&#xff0c;对玩家提供经济激励&#xff0c…...

【Docker】搭建安全可控的自定义通知推送服务 - Bark

【Docker】搭建安全可控的自定义通知推送服务 - Bark 前言 本教程基于绿联的NAS设备DX4600 Pro的docker功能进行搭建。 简介 Bark是一款为Apple设备用户设计的开源推送服务应用&#xff0c;它允许开发者、程序员以及一般用户将信息快速推送到他们自己的iPhone、iPad等设备上…...

国内IP代理软件电脑版:深入解析与应用指南

随着互联网技术的快速发展&#xff0c;网络活动日益丰富多样&#xff0c;IP代理软件也因其独特的功能和优势&#xff0c;成为许多电脑用户不可或缺的工具。在国内&#xff0c;由于网络环境的复杂性和特殊性&#xff0c;选择一款稳定、高效的IP代理软件电脑版尤为重要。虎观代理…...

面向对象设计之开闭原则

设计模式专栏&#xff1a; http://t.csdnimg.cn/4Mt4u 目录 1.引言 2.如何理解“对扩展开放、对修改关闭” 3.修改代码就意味着违反开闭原则吗 4.如何做到“对扩展开放、对修改关闭” 5.如何在项目中灵活应用开闭原则 6.总结 1.引言 开闭原则(Open Closed Principle&…...

【项目技术介绍篇】若依项目代码文件结构介绍

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…...

网站建设实训报告/sem是什么意思中文

use关键字在php中的使用1、use最常用在给类取别名&#xff0c;还可以用在闭包函数中&#xff0c;代码如下function test() {$a hello;return function ($a)use($a) {echo $a . $a;};}$b test();$b(world);//结果是hellohello当运行test函数&#xff0c;test函数返回闭包函数&…...

泰国网站的域名/黑科技引流软件是真的吗

以下为免安装版MySQL的配置步骤 版本号&#xff1a;mysql-5.7.23-winx64 第一步&#xff1a;进入官网下载免安装MySQL的压缩包 下载地址&#xff1a;点我跳转下载 点击红色框内不登录即可进行下载 第二步&#xff1a;将下载好的压缩包解压到自己创建的文件夹内 解压后点进去…...

重庆点优定制网站建设/快速优化排名公司推荐

怎么实现高可用呢&#xff1f; 最重要的一点就是冗余数据啊&#xff0c;redis 是通过主从复制来实现数据的冗余存储&#xff0c;这样在主redis down调用之后&#xff0c;切换到从就可以了&#xff0c;这样就实现了故障转移&#xff0c;保证了高可用了&#xff0c;今天我们主要来…...

网站内容授权书/买友情链接有用吗

Integer&int int是Java的基本数据类型&#xff0c;而Integer是它的包装类&#xff0c;在进行比较时&#xff0c;如果是基本类型&#xff0c;比较的是值&#xff0c;如果是引用类型&#xff0c;比较的是地址&#xff0c;也就是是否是同一个对象。 public static void main…...

wordpress 双侧边栏/黄页88网推广服务

我的SQL SEVER 2005是2010年12装的 那时候一直在用&#xff0c;后来自己的PC不想用来编程了所以就把 PC上面的SQL服务都给禁掉了。现在的情况是怎么都连不上本地数据库sqlexpress倒是可以连接上。有没有什么办法能 解决这个问题&#xff0c; 请大家帮忙解决一下。标题: 连接到…...

微信网站/免费加精准客源

在Java&#xff0c;尽管有类库Arrays.sort(arr)可以让我们对数组进行排序&#xff0c;当我们参加笔试时&#xff0c;要对一些经典排序算法进行书写&#xff0c;这就要求我们能够写出一些基本的排序算法;本文想讲下希尔排序&#xff0c;但在希尔排序前&#xff0c;我们先谈谈插入…...