【Linux】线程互斥 -- 互斥锁 | 死锁 | 线程安全
- 引入
- 互斥
- 初识锁
- 互斥量mutex
- 锁原理解析
- 可重入VS线程安全
- STL中的容器是否是线程安全的?
- 死锁
引入
我们写一个多线程同时访问一个全局变量的情况(抢票系统),看看会出什么bug:
// 共享资源, 火车票
int tickets = 10000;
//新线程执行方法
void *getTicket(void *args)
{std::string username = static_cast<const char *>(args);while (true){if (tickets > 0){usleep(1254); // 1秒 = 1000毫秒 = 1000 000 微妙 = 10^9纳秒std::cout << username << " 正在进行抢票: " << tickets << std::endl;// 用这段时间来模拟真实的抢票要花费的时间tickets--;}else{break;}}return nullptr;
}
//主线程
//跟之前一样创建多个线程然后调用这个getTicket方法就行
假如创建4个线程同时抢票,总票数有10000张,每个线程抢到票以后减一,按照正常情况我们应该是抢票到0截至。
多个线程交叉执行本质:就是让调度器尽可能的频繁发生线程调度与切换
线程一般在什么时候发生切换呢?时间片到了,来了更高优先级的线程,线程等待的时候。
线程是在什么时候检测上面的问题呢?从内核态返回用户态的时候,线程要对调度状态进行检测,如果可以,就直接发生线程切换
实验结果:假如此时tickets = 1
,第1号线程先判断了if (tickets > 0)
然后进入语句中,结果被usleep
阻塞了,这时候切换了另外的线程,此时tickets
的值还未被1号进程更改,所以它同样也能进入语句中,就这样导致tickets--
被执行了多次,然后就出现上述的负数结果。
对变量进行++,或者–,在C、C++上,看起来只有一条语句,但是汇编之后至少是三条语句:
- 从内存读取数据到CPU寄存器中
- 在寄存器中让CPU进行对应的算逻运算
- 写回新的结果到内存中变量的位置
模拟一下线程1与线程2的更改切换逻辑:
我们定义的全局变量,在没有保护的时候,往往是不安全的,像上面多个线程在交替执行造成的数据安全问题,发生了数据不一致问题!
互斥
初识锁
- 临界资源:多个执行流进行安全访问的共享资源
- 临界区:我们把多个执行流中,访问临界资源的代码(往往是线程代码的很小的一部分)
- 互斥:想让多个线程串行访问共享资源,任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
- 原子性:对一个资源进行访问的时候,要么不做,要么做完,该操作只有两态,不会被任何调度机制打断的操作。(只用一条汇编语句就能执行完的算是原子,当然还有别的情况之后再详细说明)
初步使用加锁解锁:
//定义为全局的锁,无需初始化和销毁,直接以这样的形式使用:
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *getTicket(void *args)
{std::string username = static_cast<const char *>(args);while (true){pthread_mutex_lock(&lock);if (tickets > 0){usleep(1254); // 1秒 = 1000毫秒 = 1000 000 微妙 = 10^9纳秒std::cout << username << " 正在进行抢票: " << tickets << std::endl;tickets--;pthread_mutex_unlock(&lock);}else{pthread_mutex_unlock(&lock);break;}}return nullptr;
}
运行现象:
我们可以很明显感受到,运行的速度变慢了,而且这里的线程4一直在抢票,别的线程没有机会
- 加锁和解锁的过程多个线程串行执行的,程序变慢了!
- 锁只规定互斥访问,没有规定必须让谁优先执行
- 锁就是真是的让多个执行流进行竞争的结果
线程解锁后,立马又申请锁,导致别的线程竞争不过,我们可以在每次循环末尾增加一段阻塞时间:
如何看待锁?
- 锁本身就是一个共享资源(我们一份代码要使用锁,前提是我们能看到这个锁,这个资源),全局的变量是要被保护的,锁是用来保护全局的资源的,锁本身也是全局资源,锁的安全谁来保护呢?
- pthread_mutex_lock、pthread_mutex_unlock:加锁的过程必须是安全的!加锁的过程其实是原子的!
- 如果申请成功,就继续向后执行,如果申请暂时没有成功,执行流会阻塞!
- 谁持有锁,谁进入临界区!
对锁的思考:
- 如果线程1,申请锁成功,进入临界资源,正在访问临界资源期间,其他线程在做什么?阻塞等待
- 如果线程1,申请锁成功,进入临界资源,正在访问临界资源期间,线程1可不可以被切换呢?绝对可以的。当持有锁的线程被切走的时候,是抱着锁被切走的,即便自己被切走了,其他线程依旧无法申请锁成功,也便无法向后执行,直到我最终释放这个锁!
所以,对于其他线程而言,有意义的锁的状态,无非两种:1.申请锁前2.释放锁后。站在其他线程的角度看待当前线程持有锁的过程,就是原子的!
未来我们在使用锁的时候,一定要尽量保证临界区的粒度(锁中间保护代码的多少)要非常小!
有人可能会想,加锁也未必安全,比如我让线程12加锁去访问公共资源,线程3不加锁去访问公共资源,这样的话公共资源依旧没有被保护起来。加锁是程序员行为,必须做到要加就都要加
互斥量mutex
- 大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。(这种说法其实不准确,如果硬要访问还是有可能做到)
- 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互。
- 多个线程并发的操作共享变量,会带来一些问题。
--
操作并不是原子操作,而是对应三条汇编指令:
load
:将共享变量ticket从内存加载到寄存器中update
: 更新寄存器里面的值,执行-1操作store
:将新值,从寄存器写回共享变量ticket的内存地址
要解决以上问题,需要做到三点:
- 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
- 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
- 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。
要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量
初始化互斥量有两种方法:
- 静态分配:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
- 动态分配:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);
参数:
mutex:要初始化的互斥量
attr:NULL
销毁互斥量需要注意:
- 使用
PTHREAD_MUTEX_INITIALIZER
初始化的互斥量不需要销毁 - 不要销毁一个已经加锁的互斥量
- 已经销毁的互斥量,要确保后面不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥量加锁和解锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误码
调用 pthread_ lock
时,可能会遇到以下情况:
- 互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功
- 发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread_ lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁。
锁原理解析
经过上面的例子,大家已经意识到单纯的
i++
或者++i
都不是原子的,有可能会有数据一致性问题为了实现互斥锁操作,大多数体系结构都提供了swap
或exchange
指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。 现在我们把lock
和unlock
的伪代码改一下
加锁执行图:
接下来的代码就是判断,寄存器中的代码与数据是否符合。
在线程1执行后面的内容时,时刻都可能被切换,但是切换了线程2,寄存器的内容也会被切换掉,这样就算怎么执行,线程2寄存器中始终都是0。再切换回线程1,由上下文保护,寄存器内容切换回原来的数字1。
解锁:movb $1, mutex
:就是将1重新给mutex
这个mutex原本的1就像是一个令牌,它有且只有一个,谁先抢到就能先运行
可重入VS线程安全
线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题。
重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。
常见的线程不安全的情况:
- 不保护共享变量的函数
- 函数状态随着被调用,状态发生变化的函数
- 返回指向静态变量指针的函数
- 调用线程不安全函数的函数
常见的线程安全的情况:
- 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的
- 类或者接口对于线程来说都是原子操作
- 多个线程之间的切换不会导致该接口的执行结果存在二义性
常见不可重入的情况:
- 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
- 调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构
- 可重入函数体内使用了静态的数据结构
常见可重入的情况:
- 不使用全局变量或静态变量
- 不使用用malloc或者new开辟出的空间
- 不调用不可重入函数
- 不返回静态或全局数据,所有数据都有函数的调用者提供
- 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
可重入与线程安全联系:
- 函数是可重入的,那就是线程安全的
- 函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题
- 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。
可重入与线程安全区别:
- 可重入函数是线程安全函数的一种
- 线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
- 如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。
STL中的容器是否是线程安全的?
不是安全的,原因是: STL
的设计初衷是将性能挖掘到极致,而一旦涉及到加锁保证线程安全,会对性能造成巨大的影响。而且对于不同的容器,加锁方式的不同,性能可能也不同(例如hash表的锁表和锁桶)。因此 STL 默认不是线程安全,如果需要在多线程环境下使用,往往需要调用者自行保证线程安全。
智能指针是否是线程安全的?
对于 unique_ptr
,由于只是在当前代码块范围内生效,因此不涉及线程安全问题。
对于 shared_ptr
,多个对象需要共用一个引用计数变量,所以会存在线程安全问题。但是标准库实现的时候考虑到了这个问题,基于原子操作(CAS
)的方式保证 shared_ptr
能够高效,原子的操作引用计数。
死锁
死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
感性认识死锁:小明与小红各自有5毛钱,它们一起去商店买棒棒糖,商店老板说它们的棒棒糖1块钱一个,小明对小红说能不能把她的5毛钱给他,这样他将能凑1块钱买棒棒糖,小红不乐意,她反问小明能不能把他的5毛钱给自己,这样她就能买棒棒糖,它们互相僵持,最后都没买到棒棒糖。他们之前出现的互相僵持的情况就是死锁
死锁四个必要条件:
- 互斥条件:一个资源每次只能被一个执行流使用
- 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
- 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
避免死锁方法:
- 破坏死锁的四个必要条件
- 加锁顺序一致
- 避免锁未释放的场景
- 资源一次性分配
避免死锁算法:死锁检测算法、银行家算法
如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀
相关文章:
![](https://img-blog.csdnimg.cn/7c9d14a671294a6684c5c6ded88297d5.png)
【Linux】线程互斥 -- 互斥锁 | 死锁 | 线程安全
引入互斥初识锁互斥量mutex锁原理解析 可重入VS线程安全STL中的容器是否是线程安全的? 死锁 引入 我们写一个多线程同时访问一个全局变量的情况(抢票系统),看看会出什么bug: // 共享资源, 火车票 int tickets 10000; //新线程执行方法 vo…...
![](https://www.ngui.cc/images/no-images.jpg)
【vue-pdf】PDF文件预览插件
1 插件安装 npm install vue-pdf vue-pdf GitHub:https://github.com/FranckFreiburger/vue-pdf#readme 参考文档:https://www.cnblogs.com/steamed-twisted-roll/p/9648255.html catch报错:vue-pdf组件报错vue-pdf Cannot read properti…...
![](https://img-blog.csdnimg.cn/e206b36a957247058876d0a1601ca415.png)
Flink集群运行模式--Standalone运行模式
Flink集群运行模式--Standalone运行模式 一、实验目的二、实验内容三、实验原理四、实验环境五、实验步骤5.1 部署模式5.1.1 会话模式(Session Mode)5.1.2 单作业模式(Per-Job Mode)5.1.3 应用模式(Application Mode&a…...
![](https://www.ngui.cc/images/no-images.jpg)
Spring整合JUnit实现单元测试
Spring整合JUnit实现单元测试 一、引言 在软件开发过程中,单元测试是保证代码质量和稳定性的重要手段。JUnit是一个流行的Java单元测试框架,而Spring是一个广泛应用于Java企业级开发的框架。本文将介绍如何使用Spring整合JUnit实现单元测试,…...
![](https://www.ngui.cc/images/no-images.jpg)
Spring Boot学习路线1
Spring Boot是什么? Spring Boot是基于Spring Framework构建应用程序的框架,Spring Framework是一个广泛使用的用于构建基于Java的企业应用程序的开源框架。Spring Boot旨在使创建独立的、生产级别的Spring应用程序变得容易,您可以"只是…...
![](https://img-blog.csdnimg.cn/4d7f67d001f445c5b2bffa6c74fbfa75.gif#pic_center)
管理类联考——写作——论说文——实战篇——标题篇
角度3——4种材料类型、4个立意对象、5种写作态度 经过审题立意后,我们要根据我们的立意,确定一个主题,这个主题必须通过文章的标题直接表达出来。 标题的基本要求 主题清晰,态度明确 第一,阅卷人看到一篇论说文的标…...
![](https://img-blog.csdnimg.cn/0f4dd94aab0f40e98bf642ad1ac3875e.png)
idea中设置maven本地仓库和自动下载依赖jar包
1.下载maven 地址:maven3.6.3 解压缩在D:\apache-maven-3.6.3-bin\apache-maven-3.6.3\目录下新建文件夹repository打开apache-maven-3.6.3-bin\apache-maven-3.6.3\conf文件中的settings.xml编辑:新增本地仓库路径 <localRepository>D:\apache-…...
![](https://img-blog.csdnimg.cn/31df673b9070470480223ccbe5d4f4ab.png)
前缀和差分
前缀和 前缀和:一段序列里的前n项和 给出n个数,在给出q次问询,每次问询给出L、R,快速求出每组数组中一段L至R区间的和 给出一段数组,每次问询为求出l到r区间的和 普通方法:L到R进行遍历,那么…...
![](https://img-blog.csdnimg.cn/d0ac8cc0f0244ef3831e1d7f57ba8364.png)
Golang GORM 模型定义
模型定义 参考文档:https://gorm.io/zh_CN/docs/models.html 模型一般都是普通的 Golang 的结构体,Go的基本数据类型,或者指针。 模型是标准的struct,由Go的基本数据类型、实现了Scanner和Valuer接口的自定义类型及其指针或别名组成&#x…...
![](https://img-blog.csdnimg.cn/img_convert/364289d474972df4d0482de116eb37b1.png)
微服务的各种边界在架构演进中的作用
演进式架构 在微服务设计和实施的过程中,很多人认为:“将单体拆分成多少个微服务,是微服务的设计重点。”可事实真的是这样吗?其实并非如此! Martin Fowler 在提出微服务时,他提到了微服务的一个重要特征—…...
![](https://www.ngui.cc/images/no-images.jpg)
使用 docker-compose 一键部署多个 redis 实例
目录 1. 前期准备 2. 导入镜像 3. 部署redis master脚本 4. 部署redis slave脚本 5. 模板文件 6. 部署redis 7. 基本维护 1. 前期准备 新部署前可以从仓库(repository)下载 redis 镜像,或者从已有部署中的镜像生成文件: …...
![](https://img-blog.csdnimg.cn/b7bcb86da5c043c7b84dfec6197ed4e5.png)
14-测试分类
1.按照测试对象划分 ①界面测试 软件只是一种工具,软件与人的信息交流是通过界面来进行的,界面是软件与用户交流的最直接的一层,界面的设计决定了用户对设计的软件的第一印象。界面如同人的面孔,具有吸引用户的直接优势…...
![](https://img-blog.csdnimg.cn/957554eb37cd4dc797faba23303d613a.png)
打开域名跳转其他网站,官网被黑解决方案(Linux)
某天打开网站,发现进入首页,马上挑战到其他赌博网站。 事不宜迟,不能让客户发现,得马上解决 我的网站跳转到这个域名了 例如网站跳转到 k77.cc 就在你们部署的代码的当前文件夹下面,执行下如下命令 find -type …...
![](https://www.ngui.cc/images/no-images.jpg)
redis总结
1.redis redis高性能的key-value数据库,支持持久化,不仅仅支持简单的key-value,还提供了list,set,zset,hash等数据结构的存储,支持数据的备份(master-slave模式) redis&…...
![](https://img-blog.csdnimg.cn/img_convert/ca59f50d2aa9c1fa22e1ec34bccbe171.png)
现代C++中的从头开始深度学习:激活函数
一、说明 让我们通过在C中实现激活函数来获得乐趣。人工神经网络是生物启发模型的一个例子。在人工神经网络中,称为神经元的处理单元被分组在计算层中,通常用于执行模式识别任务。 在这个模型中,我们通常更喜欢控制每一层的输出以服从一些约束…...
![](https://img-blog.csdnimg.cn/b7084e2b3dd84c6ca78c1fb19413b67d.jpeg)
python怎么实现tcp和udp连接
目录 什么是tcp连接 什么是udp连接 python怎么实现tcp和udp连接 什么是tcp连接 TCP(Transmission Control Protocol)连接是一种网络连接,它提供了可靠的、面向连接的数据传输服务。 在TCP连接中,通信的两端(客户端和…...
![](https://www.ngui.cc/images/no-images.jpg)
java设计模式-观察者模式(jdk内置)
上一篇我们学习了 观察者模式。 观察者和被观察者接口都是我们自己定义的,整个设计模式我们从无到有都是自己设计的,其实,java已经内置了这个设计模式,我们只需要定义实现类即可。 下面我们不多说明,直接示例代码&am…...
![](https://img-blog.csdnimg.cn/img_convert/8a7f2488c79e75a2cbdb3fe9344adcf9.gif)
秒级体验本地调试远程 k8s 中的服务
点击上方蓝色字体,选择“设为星标” 回复”云原生“获取基础架构实践 背景 在这个以k8s为云os的时代,程序员在日常的开发过程中,肯定会遇到各种问题,比如:本地开发完,需要部署到远程k8s集群,本地…...
![](https://img-blog.csdnimg.cn/img_convert/4e3e7c15d4a9b1576d005058ff4e1d73.png)
CV前沿方向:Visual Prompting 视觉提示工程下的范式
prompt在视觉领域,也越来越重要,在图像生成,作为一种可控条件,增进交互和可控性,在多模态理解方面,指令prompt也使得任务灵活通用。视觉提示工程,已然成为CV一个前沿方向! 下面来看看…...
![](https://www.ngui.cc/images/no-images.jpg)
Redis五大基础类型解析
1.String类型 特征:即存储字符串的类型,单个字符串存储量最大不超过512MB 常用业务场景:⽤来存储JSON序列化之后对象 底层编码: int编码 数据结构特点:ptr指针直接指向字符串常量池中对应字符串地址,而…...
![](https://www.ngui.cc/images/no-images.jpg)
在CSDN学Golang云原生(服务网格istio)
一,在Kubernetes上部署istio 在Kubernetes上部署istio,可以按照以下步骤进行: 安装Istio 使用以下命令从Istio官网下载最新版本的Istio: curl -L https://istio.io/downloadIstio | ISTIO_VERSION<VERSION> sh - 其中&…...
![](https://www.ngui.cc/images/no-images.jpg)
Golang 获取本地 IP 地址方法
在 Golang 中,使用 net 包可以很方便地获取到本机IP地址。 借助 net.InterfaceAddrs 方法 简单示例代码如下: package mainimport ("fmt""net" )func main() {addrList, err : net.InterfaceAddrs()if err ! nil {panic(err)}for…...
![](https://img-blog.csdnimg.cn/40b5e3074ab44b13b3eace4a8ab157bb.gif)
抖音seo短视频账号矩阵系统技术开发简述
说明:本开发文档适用于抖音seo源码开发,抖音矩阵系统开发,短视频seo源码开发,短视频矩阵系统源码开发 一、 抖音seo短视频矩阵系统开发包括 抖音seo短视频账号矩阵系统的技术开发主要包括以下几个方面: 1.前端界面设…...
![](https://img-blog.csdnimg.cn/c6509d9d3a8e4a9e916a60fd9abca45e.png)
运维高级--shell脚本完成分库分表
为什么要进行分库分表 随着系统的运行,存储的数据量会越来越大,系统的访问的压力也会随之增大,如果一个库中的表数据超过了一定的数量,比如说MySQL中的表数据达到千万级别,就需要考虑进行分库分表; 其…...
![](https://www.ngui.cc/images/no-images.jpg)
Mysql 忘记密码怎么重置密码(详细步骤)
每种方法都有其适用的情况,根据具体情况选择合适的方法。无论选择哪种方法,请务必在重置密码后及时删除临时用户并重新启动 MySQL 服务。 一、使用 mysqladmin 重置密码 停止服务 # systemctl 启动的使用这个停止 $ sudo systemctl stop mysql# mac 本机…...
![](https://img-blog.csdnimg.cn/bec187dcf3794afb97fe2ea3b7180fa2.png)
机器学习深度学习——图像分类数据集
👨🎓作者简介:一位即将上大四,正专攻机器学习的保研er 🌌上期文章:机器学习&&深度学习——softmax回归(下) 📚订阅专栏:机器学习&&深度学习…...
![](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4f7d2562-49df-4956-9ae7-8351c56d1820/Untitled.png)
【PWN · 栈迁移】[BUUCTF]ciscn_2019_es_2
第一道栈迁移题目,跌跌撞撞理解了 前言 当前溢出可用空间比较少时(极端情况下仅能覆写ebp和ret),可以通过栈迁移的方式,扩大shellcode的容纳空间,其核心是将esp移动到一段shellocode开头。而esp总是由ebp赋…...
![](https://www.ngui.cc/images/no-images.jpg)
网络编程(13): 网络通信常用命令(后续待补充)
ifconfig 一般用于查看网卡信息 ping 一般用于侦测本机到目标网络主机的网络是否通常: ping ip/域名 telnet 可以用于指定ip地址和端口的侦听服务是否存在:telnet ip port, 也可以模拟客户端给服务器发数据 netstat 用于查看网络连接状态 -a: 显示所有选项 -t&#…...
![](https://img-blog.csdnimg.cn/f322a4a68dd14d2ea864a16c1daf7103.png#pic_center)
flask创建数据库连接池
flask创建数据库连接池 在Python中,您可以使用 Flask-SQLAlchemy 这个扩展来创建一个数据库连接池。Flask-SQLAlchemy 是一个用于 Flask 框架的 SQLAlchemy 操作封装,实现了 ORM(Object Relational Mapper)。ORM 主要用于将类与数据库中的表建立映射关系…...
![](https://img-blog.csdnimg.cn/218694ffae7447f2babd3817770ab72d.png)
C语言手撕顺序表
目录 一、概念 1、静态顺序表:使用定长数组存储元素。 2、动态顺序表:使用动态开辟的数组存储 二、接口实现 1、对顺序表的初始化 2、对数据的销毁 3、对数据的打印 4、检查是否需要扩容 5、尾插 6、头插 7、尾删 8、头删 9、在pos位置插入x …...
![](http://farm9.staticflickr.com/8146/7169024725_ec0d182348_z.jpg)
怎么用二维动画做网站首页步骤/爱站网怎么用
在MySQL服务器出现短暂(5~30秒)的性能波动的时候,一般的性能监控工具都很难抓住故障现场,也就很难收集对应较细粒度的诊断信息。另外,如果这种波动出现的频率很低,例如几天才一次,我们也很难人为的抓住现场,…...
![](https://img-blog.csdnimg.cn/img_convert/5285d019160943233f96fed7fc06ed16.webp?x-oss-process=image/format,png)
网页添加兼容性站点/色盲测试图
基于阿里巴巴JAVA开发规范整理 一、命名风格 【强制】类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO 正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion 反例ÿ…...
![](/images/no-images.jpg)
网站开发的三层架构/长春网络优化哪个公司在做
值栈包含两个 context(域对象框架存入) 和根对象(push,set) //源码dofilter----actionContext push通过压栈后的数据是在最顶层,用 value"top" 来取值即可; 值栈后进先出的原则 set和map的混到一起 从上到下取值;[0].name; set一般是集合;push一般是属性 1.获取值…...
![](https://s1.51cto.com/images/blog/201902/03/38c3af00e6dd246571387d317bcdb4ba.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
新闻类网站怎么建设/东莞网络优化哪家好
最近听圈内朋友说了这么一句话:“现在创业公司比员工还多!”作为创业公司的创始人,好多难题摆在眼前: 新招来的销售经理没有太多销售经验,成天在外跑客户却不见数字,怎么办? 新兴行业销售人才缺…...
![](/images/no-images.jpg)
用凡科建设的网站安全吗/全渠道营销管理平台
我有一个tiff图片存储在内存中(在javascript变量中).在浏览器窗口中显示此图像的javascript或html技术是什么?是否有“ drawimage”类型的功能?解决方法:本地浏览器对tiff文件的支持仍然很糟糕.Wikipedia很好地概述了Image format support浏览器.话虽如此…...
![](https://img-blog.csdnimg.cn/img_convert/6161a1e068ba6b4b98e7a15e3f567108.png)
毕业设计做网站难吗/百度通用网址
2年前,2018年3月12日微信公众号宣布取消留言功能,新注册的账号一律都没有留言功能了,让很多运营者大呼头疼。3天前,2020年8月18日微信公众号推出内测问题功能,可以在文章中和用户互动,解决了很多运营者没办…...