【Linux】线程的互斥
一、进程线程间的互斥相关的背景概念
- 临界资源:多线程执行流共享的资源就叫做临界资源
- 临界区:每一个线程内部,访问临界资源的代码,就叫做临界区
- 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界区资源,通常对临界资源起保护作用
- 原子性:不会被任何调度机制打断的操作,该操作只有两个状态:要么完成,要么未完成
二、互斥量 mutex
- 大部分情况下,线程使用的数据都是局部变量,变量的地址空间在线程栈空间中,这种情况下,变量归属于单个线程,其他线程无法获得这种变量。
- 但是有的时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互。
- 多个线程并发的操作共享变量,会带来一些问题。
三、举例代码
3.1 先通过一个例子来看一看:
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <vector>
#include "Thread.hpp"using namespace Mypthread;int tickets = 10000; // 票数void route(const std::string &name)
{while (true){if (tickets > 0){usleep(1000); // 微妙级,用1毫秒表示抢票的时间printf("who: %s , get tickets: %d\n", name.c_str(), tickets);tickets--;}elsebreak;}
}// 抢票检验线程互斥
int main()
{mThread t1("thread-1", route);mThread t2("thread-2", route);mThread t3("thread-3", route);mThread t4("thread-4", route);t1.Start();t2.Start();t3.Start();t4.Start();t1.Join();t2.Join();t3.Join();t4.Join();return 0;
}
3.2 解释现象:
3.2.1 判断的过程是不是一种计算
计算机常用的数据类型分为两种:算术运算,逻辑运算。CPU在计算过程中,其操作过程不是原子的,是需要分成好几个步骤的。
我们来拿逻辑判断来举例:
- 第一步要先将数据移动到寄存器中
- 第二步进行逻辑判断
- 第三步将结果发出
在计算机中,CPU有时只有一套,但是寄存器中的数据是可以有多套的。多个线程根据时间片进行交替执行,因为寄存器中的数据属于线程私有,看起来是放在一套共有的寄存器中,但是当线程要被切走的时候,线程是需要带走自己的数据,当线程回来的时候,线程需要将数据进行恢复。
3.2.2 自减--的原理(不是原子的)
- 重读数据
- 减减数据
- 写回数据
3.2.3 在上面内容的基础上,我们来解释一下这个现象
假设票数只剩下一张,进程A看见还有一张,进行买票;但是在买票的过程中,突然被切出,进程B进入买票,梅开二度,在买票的过程中,突然被切出...当进程A恢复线程,进行减减操作,票数为0,退出;进程B恢复线程,记住,现在tickets的票数为0,减减重读数据,将tickets为0读入,将tickets减为负数,之后的线程同理。
3.3 为什么可能无法获得争取结果?
- if 语句判断条件为真以后,代码可以并发的切换到其他线程
- usleep 这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段
- --ticket 操作本身就不是一个原子操作
3.4 我们来看一看汇编代码
通过汇编代码,我们可以发现这种操作不是原子性操作,而是分别对应于三条汇编指令:
- load:将共享变量ticket从内存中加载到寄存器中
- update:更新寄存器里面的值,执行 -1 操作
- store:更新值,从寄存器写回共享内存ticket的内存地址
3.5 解决以上问题的措施
要解决以上问题,需要做到三点:
- 代码必须有互斥行为:当代码进行临界区执行时,不允许其他线程进入该临界区
- 如果多个线程同时要求执行临界区的代码,并且临界区没有线程执行,那么只能允许一个线程进入给临界区
- 如果线程不在临界区中执行,那么线程不能组织其他线程进去临界区
四、互斥量
要做到上述三点,本质上需要一把锁。Linux上提供的这把锁叫做互斥量。
4.1 互斥量的接口
4.1.1 互斥量的函数
函数的原型:
#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);// 在全局开辟的或者在静态区中开辟的 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
函数的功能:
该函数用于C函数的多线程编程中,互斥锁的初始化。在任何时候,只允许一个线程进行访问。
函数的参数:
- mutex:指向要初始化的互斥锁的变量本身
- restrictattr:指定了新建互斥锁的属性。如果参数restrictattr为空(NULL),则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。
下面是互斥锁的类型:
- PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
- PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
- PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
- PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
函数的返回值:
- 函数成功完成之后会返回零
- 其他任何返回值都表示出现了错误
4.1.2 互斥量的初始化
方法1:静态分配
// 在全局开辟的或者在静态区中开辟的
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
方法2:动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);
4.1.3 互斥量的销毁
销毁互斥量需要注意:使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要进行销毁
对于一个已经加锁的互斥量不要进行销毁,对于已经销毁的互斥量,要确保后面不会有线程在尝试加锁。
函数的原型:
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t* mutex);
函数的功能:
进行互斥量的销毁,对于一个已经加锁的互斥量不要进行销毁,对于已经销毁的互斥量,要确保后面不会有线程在尝试加锁。
函数的参数:
- mutex:指向要初始化的互斥锁的变量本身
函数的返回值:
- 函数成功完成之后会返回零
- 其他任何返回值都表示出现了错误
4.1.4 互斥量的加锁和解锁
4.1.4.1 互斥量的加锁(阻塞调用)
函数的原型:
#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t* mutex);
函数的功能:
该函数用于对互斥锁进行加锁操作。它阻塞调用线程,直到可以获得互斥锁为止。如果互斥锁已经被其他线程锁定,则调用线程将被阻塞,直到互斥锁被解锁。
函数的参数:
- mutex:指向要初始化的互斥锁的变量本身
函数的返回值:
- 返回值为0表示成功加锁
- 返回值为非零值表示失败
4.1.4.2 互斥量的加锁(非阻塞调用)
函数的原型:
#include <pthread.h>int pthread_mutex_trylock(pthread_mutex_t* mutex);
函数的功能:
该函数是pthread_mutex_lock函数的非阻塞版本。如果mutex参数所指定的互斥锁已经被锁定的话,调用pthread_mutex_trylock函数不会阻塞当前线程,而是立即返回一个值来描述互斥锁的状况。
函数的参数:
- mutex:指向要初始化的互斥锁的变量本身
函数的返回值:
- 函数成功完成之后会返回零
- 其他任何返回值都表示出现了错误
4.1.4.3 互斥量的解锁
函数的原型:
#include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t* mutex);
函数的功能:
该函数用于解锁互斥锁,它接收一个指向互斥锁的指针作为参数,并将该互斥锁解锁。
函数的参数:
- mutex:指向要初始化的互斥锁的变量本身
函数的返回值:
- 函数成功完成之后会返回零
- 其他任何返回值都表示出现了错误
总结:
所谓对临界资源进行保护,本质上是对临界区代码进行保护!我们对所有资源经访问,本质都是通过代码进行访问。保护资源本质上就是想办法把访问资源的代码进行保护起来。
4.1.5 对抢票代码进行加锁操作
4.1.5.1 全局变量的锁
代码如下:
int tickets = 10000; // 票数// 建立一个全局范围的锁
pthread_mutex_t mutex;void route(const std::string &name)
{while (true){// 加锁pthread_mutex_lock(&mutex);if (tickets > 0){usleep(1000); // 微妙级,用1毫秒表示抢票的时间printf("who: %s , get tickets: %d\n", name.c_str(), tickets);tickets--;// 解锁pthread_mutex_unlock(&mutex);}else{// 解锁pthread_mutex_unlock(&mutex);break;}}
}
代码执行结果:
4.1.5.2 局部变量的锁
// 在Thraed类中加入锁的变量class ThreadDate{public:ThreadDate(const std::string& name, pthread_mutex_t* lock):_name(name),_lock(lock){}public:std::string _name;pthread_mutex_t* _lock;};
4.1.6 对锁进行封装
在想要保护的临界区前面进行定义一个锁即可,因为随着函数范围的释放,该锁也会跟着解锁。
class LockGuard
{
public:LockGuard(pthread_mutex_t* mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}~LockGuard(){pthread_mutex_unlock(_mutex);}private:pthread_mutex_t* _mutex;
};
4.1.7 解决历史问题
- 加锁的范围的粒度一定要尽量小
- 任何线程要进行抢票,都要先申请锁,原则上,不应该有例外
- 所有线程申请锁,前提是所有线程都要看到这把锁,锁本身也是共享资源,加锁的过程必须是原子的
- 原子性:要么不做,要么做就要做完,没有中间状态,就是原子性
- 如果线程申请锁失败了,我的线程要被阻塞
- 如果线程申请锁成功了,继续向后进行运行
- 如果线程申请锁成功了,执行临界区的代码,在执行临界区的代码期间,可以进行切换,其他线程无法进入!因为我虽然被切换,但是我没有进行释放,我可以放心的执行完毕,没有人可以打扰我。
总结:所以对于其他线程,要么我没有申请锁,要么我释放了所,对其他线程才有意义。我访问临界区,对于其他线程是原子的。
4.3 对互斥量的原理实现
我们对于锁来说,重要的函数是:
int pthread_mutex_lock(pthread_mutex_t* mutex);
如何理解申请锁成功,允许进入临界区?申请锁成功,pthread_mutex_lock函数会返回。
如何理解申请锁失败,不允许进行临界区? 申请锁时报,pthread_mutex_lock函数不会返回。
4.4 互斥量的实验原理探究
在经过上面的例子中,我们已经可以意识到单纯的 i++ 操作或者 ++i 操作都不是原子的,有可能会有数据一致性的问题,所以为了实现互斥锁的操作,大多数体系结构都提供了swap操作和exchange操作指令。
swap操作和exchange操作指令:这种指令就是原子性的,该指令的作用是把寄存器和内存单元的数据济宁相互交换,由于只有一条指令,保证了原子性,即使是多处理平台进行访问,访问内存的总线周期也有先后,一个处理器上的交换指令执行时,另一个交换指令只能等待总线周期。
现在,我们来看一下lock和unlock的伪代码进行查看:
先来看一看lock中的操作:
lock在内存中开辟一段空间,我们可以在这段空间中放入一些值,来使线程具有互斥性。比如:当lock中的数字为1,说明我们可以进行该临界区,同时将lock中的数字进行交换变为0;当lock中的数字变为0,说明我们不能进入该临界区,需要进行挂起等待,然后重新进行lock的判断。
举个例子:首先,这个锁处于无人状态。现在有一个线程A进行lock中,将lock中的数字和al寄存器的数字进行交换,然后现在al寄存器中的数字为1大于0,说明我们可以进入临界区中。之后随着时间片的轮转,线程A要被切走,对于线程A在CPU寄存器中的所有数据也要跟着一起切走,然后此时,lock中的数字为依旧为0,其他线程中的al寄存器中的数字也为0,所以其他线程无法进行该临界区,也就是将其他线程全部都锁在了外面,只有当线程A完成打开锁之后,其他线程才能进入该临界区中。
再来看一看umlock中的操作:
我们在来看一看这个unlock操作,等待线程A操作完之后,我们需要直接将数字1赋值给mutex内存中,之后唤醒其他等待Mutex的线程。
总结:
- CPU的寄存器只有一套,被所有的线程共享,但是寄存器里面的数据属于执行流的上下文,属于执行零私有的数据
- CPU在执行代码的时候,一定要有对应的执行载体:线程或者进程
- 数据在内存中被所有线程共享
把数据从内存中移动到CPU寄存器中,本质上,就是把数据从共享变为线程私有。
相关文章:
【Linux】线程的互斥
一、进程线程间的互斥相关的背景概念 临界资源:多线程执行流共享的资源就叫做临界资源临界区:每一个线程内部,访问临界资源的代码,就叫做临界区互斥:任何时刻,互斥保证有且只有一个执行流进入临界区&#…...
electron如何让你窗口总是显示在最前面【mac解决全屏窗口alwaysOnTop参数不起作用】
你创建了一个使用Electron框架的应用程序,并希望它在以下情况下始终保持可见: 在切换工作区(桌面)时可见在其他应用程序之上显示当其他应用程序全屏显示时,它也显示在顶部当Keynote处于演示模式时,它也能显示在顶部 特别是当Keynote处于演示模式时,要实现这一点比较困难…...
XR和Steam VR项目合并问题
最近有一个项目是用Steam VR开发的,里面部分场景是用VRTK框架做的,还有一部分是用SteamVR SDK自带的Player预制直接开发的。 这样本身没有问题,因为最终都是通过SteamVR SDK处理的,VRTK也管理好了SteamVR的逻辑,并且支…...
uni-app:利用Vue的原型对象Vue.prototype设置全局方法及其引用
一、在main.js中设置方法checkPermission绑定到Vue.prototype 核心代码 Vue.prototype.$checkPermission function(username) {console.log(Checking permission for:, username); }; 完整代码 import App from ./App// 添加 checkPermission 方法到 Vue.prototype 上,检查…...
django接入djangorestframework-simplejwt步骤
版本:django 4.2 python: 3.8 安装 pip install djangorestframework-simplejwtuser子应用models.py文件 from django.db import models from django.contrib.auth.models import AbstractUserclass User(AbstractUser):mobile models.CharField(max_length11, u…...
前端工程化工具系列(十)—— Browserslist:浏览器兼容性配置工具
Browserslist 是一个能够在不同的前端工具间共享目标浏览器的配置,各工具根据该配置进行代码转译等操作。 具体的这些前端工具为:Autoprefixer、Babel、postcss-preset-env、eslint-plugin-compat、stylelint-no-unsupported-browser-features、postcss-…...
双列集合底层源码
tips: 竖着的箭头:重写 横着的箭头:继承...
【Ardiuno】实验使用ESP32连接Wifi(图文)
ESP32最为精华和有特色的地方当然是wifi连接,这里我们就写程序实验一下适使用ESP32主板连接wifi,为了简化实验我们这里只做了连接部分,其他实验在后续再继续。 由于本实验只要在串口监视器中查看结果状态即可,因此电路板上无需连…...
优化家庭网络,路由器无线中继配置全攻略(中兴E1600无线中继设置/如何解决没有预埋有线网络接口的问题/使用闲置路由实现WIFI扩展)
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 网络优化 📒📒 操作步骤 📒💡适用场景🚨 常见问题及解决方案⚓️ 相关链接 ⚓️📖 介绍 📖 在现代家庭生活中,WiFi已经渗透到我们生活的每一个角落,成为了日常生活中不可或缺的一部分。然而,不少用户常常遇到W…...
【ArcGIS微课1000例】0114:基于DEM地形数据整体抬升或下降高程
相关阅读:【GlobalMapper精品教程】083:基于DEM整体抬升或下降地形高程的两种方式 文章目录 一、任务分析二、栅格计算器简介三、地形整体修改四、注意事项一、任务分析 打开软件,加载配套实验数据中的0112.rar中的dem数据,如下所示,dem的高程范围为256.75~342.37米,现在…...
AGP4+ 打包运行闪退,AGP7+ 正常(has code but is marked native or abstract)
问题 安装应用,点击图标启动立马闪退! 诡异的闪退:AGP4 打包运行闪退,AGP7 正常 unity 导出的 Android 日志两个主要点: com.android.boot.App 是 Android 的 application 子类,程序入口 java.lang.Class…...
ChatGPT3.5和ChatGPT4.0、ChatGPT4o对比
一、ChatGPT3.5、ChatGPT4.0、ChatGPT4o对比 目前ChatGPT有三个主要版本,分别是ChatGPT3.5、ChatGPT4.0、ChatGPT4o,这三个版本之间有什么差异呢? 对比项ChatGPT3.5ChatGPT4.0ChatGPT4o参数数量1750亿约1万亿未公开输入文本文本、图片文本、…...
【知识拓展】HTTP、WebSocket 和 RPC:区别与使用场景详解
在工作中,HTTP、WebSocket 和 RPC 是三种常见的协议或通信方式,根据资料查阅,本文主要记录它们的区别及其适用的使用场景 HTTP(超文本传输协议) 概述 HTTP(Hypertext Transfer Protocol)是一…...
C语言printf( ) 函数和 scanf( ) 函数格式符的修饰符 “*”有什么作⽤?
一、问题 在 printf( ) 函数和 scanf( ) 函数的格式修饰符有很多,以浮点型数据为例,有%f、%lf、 %3.0f、%.4f等。不同的修饰符表示不同的含义,那么修饰符“*”有什么含义呢? 二、解答 下⾯通过例⼦来证明⼀下这个格式符在 printf…...
java 使用WebClient发送https请求
核心逻辑 绕过ssl证书检查 具体操作 话不多说上代码 // 构建WebClient public static WebClient createWebClient() throws SSLException {SslContext context SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();HttpClient htt…...
Python 中的内存管理机制
Python 的内存管理机制主要由两个部分组成:垃圾回收机制和引用计数。 垃圾回收机制主要负责检测和回收不再被使用的内存。Python 使用的是自动垃圾回收机制,也就是说程序员不需要手动释放内存。Python 的垃圾回收机制采用了引用计数的方法来追踪和回收不…...
Mac电脑重置网络命令
在Mac电脑上重置网络设置可以通过终端命令来实现。以下是几种方法,包括刷新DNS缓存、重置网络接口,以及重置Wi-Fi设置。 刷新DNS缓存 要刷新DNS缓存,可以使用以下命令: sudo dscacheutil -flushcache; sudo killall -HUP mDNSR…...
C++期末复习总结(2)
目录 1.运算符重载 2.四种运算符重载 (1)关系运算符的重载 (2) 左移运算符的重载 (3)下标运算符的重载 (4)赋值运算符的重载 3.继承的方式 4.继承的对象模型 5.基类的构造 6…...
[word] word大括号怎么打两行 #其他#其他#微信
word大括号怎么打两行 Word给用户提供了用于创建专业而优雅的文档工具,帮助用户节省时间,并得到优雅美观的结果。 一直以来,Microsoft Office Word 都是最流行的文字处理程序。 作为 Office 套件的核心程序, Word 提供了许多易…...
【python】python指南(二):命令行参数解析器ArgumentParser
一、引言 对于算法工程师来说,语言从来都不是关键,关键是快速学习以及解决问题的能力。大学的时候参加ACM/ICPC一直使用的是C语言,实习的时候做一个算法策略后台用的是php,毕业后做策略算法开发,因为要用spark&#x…...
香橙派 Orange AIpro 测评记录视频硬件解码
香橙派 Orange AIpro 测评记录视频硬件解码 香橙派官网:http://www.orangepi.cn/ 收到了一块Orange Pi AIpro开发板,记录一下我的测评~测评简介如下:1.连接网络2.安装流媒体进行硬件解码测试3.安装IO测试 简介 Orange Pi AI Pro 是香橙派联合…...
四天工作制,比你想象的更近了一点
原文:Andrew Keshner - 2024.05.30 软件公司、大型企业甚至警察部门都在试验这一看似遥不可及的概念。 教育软件公司 Kuali 的会议精简,除非绝对必要,员工尽量避免安排会议。即使有会议,也鼓励员工跳过与自己工作无关的部分。在…...
(UE4.26)UE4的FArchive序列化入门
前言 序列化(Serialize)和反序列化(UnSerialize)是程序领域常见的概念。对于这两个词汇我理解的是 序列化(Serialize): 变量值(int, float, string等基本类型, 或者Array,Map,或者更复杂的复合体)存储为一个文件(二进制流, 二进制文件, json, xml等格式…...
Inpaint9.1软件下载附加详细安装教程
软件简介: Inpaint 是个人开发者Max开发的图片处理软件,可以高效去除水印,修复照片等。使用方法和操作都很简单,非常适合不会PS等软件的小白用户。 安 装 包 获 取 地 址: Iinpaint win版:https://souurl.cn/b…...
Unity 集成 FMOD 音频管理插件 2.02
Unity 集成 FMOD 音频管理插件 2.02 3. 集成教程:3.1 设置Unity项目3.2 设置FMOD项目3.3 设置 FMOD for Unity3.4 添加声音:卡丁车引擎3.5 添加声音:氛围3.6 添加声音:音乐3.7 删除现有音频3.8 下一步 10. 脚本 API 参考10.1 基础…...
Linux下线程的互斥与同步详解
🤖个人主页:晚风相伴-CSDN博客 💖如果觉得内容对你有帮助的话,还请给博主一键三连(点赞💜、收藏🧡、关注💚)吧 🙏如果内容有误或者有写的不好的地方的话&…...
【栈】736. Lisp 语法解析
本文涉及知识点 栈 LeetCode736. Lisp 语法解析 给你一个类似 Lisp 语句的字符串表达式 expression,求出其计算结果。 表达式语法如下所示: 表达式可以为整数,let 表达式,add 表达式,mult 表达式,或赋值的变量。表达…...
什么时候用C而不用C++?
做接口只用C,千万别要C。C是编译器敏感的,一旦导出的接口里有 std::string这些东西,以及类,注定了要为各个编译器的各个版本准备独立的库。 刚好我有一些资料,是我根据网友给的问题精心整理了一份「C的资料从专业入门…...
unix环境编程编程扫描版:深度解析与实践指南
unix环境编程编程扫描版:深度解析与实践指南 在探索Unix环境编程的广阔天地时,我们如同行走在一条充满未知与奇遇的旅程中。本篇文章将从四个方面、五个方面、六个方面和七个方面,深入剖析Unix环境编程的精髓,帮助读者在编程的海…...
2024年6月8日 每周新增游戏
中医百科中药: 中医百科中药是一款非常强大的中药知识科普软件,该应用提供500多味中草药的文献资料,强大的搜索功能可根据功效、特点和关键词来快速查找中药,而且每味中药的图片、功效、主治、炮制方法等百科知识,可以很好的帮助你…...
深圳 网站建设公司/线上免费推广平台都有哪些
2019独角兽企业重金招聘Python工程师标准>>> 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-…...
昆明有哪些帮忙做网站的公司/网站设计公司
介绍 操作符map,字面理解一下,就是映射,那么这个操作符如何使用呢? 举个栗子 1. 代码示例 Observable<Integer> observable Observable.create(new ObservableOnSubscribe<Integer>() {Overridepublic void subs…...
什么人最需要建设网站/免费推广seo
以下内容均为微信内置浏览器访问的场景。本文只阐述微信分享的URL的坑,不阐述具体的代码实现和JS-SDK的具体使用。 大家有没有发现,某些网站访问的路径与最终分享的路径不同!对,没错,就是不同。 微信JS-SDK分享的URL有…...
怎么让自己做的网站别人可以访问/怎么在百度上发布自己的信息
一 用两个栈实现队列 题目描述: 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 问题分析: 先来回顾一下栈和队列的基本特点: 栈:后进先出(LIFO) 队列:…...
做电子书的网站很有名后来被关闭了/小时seo百度关键词点击器
采用ambari安装。 需要注意所有centos的yum库必须相同,因此必须固定,而不能让其动态获取。...
wap网站如何建设/进入百度
2019独角兽企业重金招聘Python工程师标准>>> 如下是tomcat的配置文件server.xml中配置Http11NioProtocol协议的示例 <Connector connectionTimeout"20000" maxThreads"1000" port"8080" protocol"org.apache.coyote.http11.H…...