【Linux从入门到精通】多线程 | 线程互斥(互斥锁)
上篇文章我们对线程 | 线程介绍&线程控制介绍后,本篇文章将会对多线程中的线程互斥与互斥锁的概念进行详解。同时结合实际例子解释了可重入与不被重入函数、临界资源与临界区和原子性的概念。希望本篇文章会对你有所帮助。
文章目录
引入
一、重入与临界
1、1 可重入与不被重入函数
1、1、1 不可重入函数
1、1、2 可重入函数
1、2 临界资源与临界区
1、2、1 临界资源
1、2、2 临界区
1、3 原子性
二、 线程互斥
2、1 分析原因与再次理解概念
2、2 互斥锁
2、2、1 什么是互斥锁
2、2、2 pthread_mutex_t
2、2、3 pthread_mutex_init 初始化
2、3 抢票加互斥锁
2、4 互斥锁总结
三、互斥锁实现原理
🙋♂️ 作者:@Ggggggtm 🙋♂️
👀 专栏:Linux从入门到精通 👀
💥 标题:线程互斥💥
❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️
引入
我们先看一段多线程抢票的代码:
int tickets=10000;void* getTickets(void* args) {while(true){if(tickets>0){usleep(1000);printf("%s: %d\n",(char*)args,tickets);tickets--;}elsebreak;}return nullptr; } int main( void ) {srand(time(nullptr));pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, getTickets, (void*)"thread 1");pthread_create(&t2, NULL, getTickets, (void*)"thread 2");pthread_create(&t3, NULL, getTickets, (void*)"thread 3");pthread_create(&t4, NULL, getTickets, (void*)"thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);return 0; }
上述代码就是创建了四个新线程同时去抢票,当票数为0时,就停止抢票。那我们看一下运行结果:
怎么票数会被抢到负数呢?上述代码明明就是在票数为0的时候就终止了。这是为什么呢?我们接着往下看。
一、重入与临界
1、1 可重入与不被重入函数
我们先看下图:
上图就是一个链表的节点插入。我们之前学的是单进程进行链表的插入,也就是但是执行流进行的插入。当然,但执行流是没有任何问题。如果现在有多个执行流在执行链表的插入呢?会不会出现意想不到的问题呢?我们看如下情况。
main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换 到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后 向链表中插入两个节点,而最后只有一个节点真正插入链表中了。我们发现就出现了一个节点的丢失。
1、1、1 不可重入函数
不可重入函数(Non-reentrant Function),它在被多个任务并发调用时可能会出现竞争条件(Race Condition)或数据不一致的问题。
在上述情况中,我们发现插入节点的函数被多个执行流执行时,发生了意想不到的错误,出现了数据丢失的问题。我们称之为该函数不可被重入。
1、1、2 可重入函数
可重入函数(Reentrant Function)是指可以被同时多个任务调用而不会发生错误或产生意外结果的函数。换句话说,可重入函数在被多个任务并发调用时,能够保持其内部状态的一致性。我们所学的大部分函数都是不可被重入的。
1、2 临界资源与临界区
1、2、1 临界资源
临界资源(Critical Resource)是指在并发编程中,多个任务或线程之间共享的某一资源,但同时只能被一个任务或线程访问和操作的资源。
1、2、2 临界区
临界区(Critical Section)是指代码中访问临界资源的那一部分代码段或区域。在临界区内部,任务或线程可以对临界资源进行读取、写入或其他操作。因为临界资源只能被一个任务或线程访问,所以必须确保在任何时刻只有一个任务或线程能够进入临界区,以避免对临界资源造成竞争条件和不确定的结果。
这里先给大家把概念引出,后面会再次结合例子来解释临界资源与临界区。
1、3 原子性
其实我们也不难发现上述情况问题的所在。一是有多个执行流在执行同一个操作。因为当一个进程在运行时,任何时刻都有可能被切换下去,去执行另一个进程。二是插入操作分为多个步骤完成。两点一结合,就是在插入没有完成的时候该执行流就被切换走了,进而导致出现问题。原子性是一个操作没有中间的状态。就是一个操作要执行就执行结束,要不就不执行,没有其他的状态。加入插入操作是原子性的,也就不会发生数据错误。
二、 线程互斥
2、1 分析原因与再次理解概念
上面我们了解了重入与临界的概念后,我们再看引入中抢票的例子。抢票例子是多线程进行抢票。也就是有多个执行流在进行抢票。我们以为tickets--在CPU內部直接减1就完了吗?实际上并不是。具体如下图:
假设线程1抢票时,此时票好有10000张。
于是就进行判断,打印。刚打印完,把tickets=10000读到寄存器中,还没有抢票(tickets--),该线程的时间片到了被切换下去。此时线程1的上下文会被保存起来。然后线程2被调度。
由于各种原因,线程2的优先级较高。线程二就行行疯狂抢票。一下子抢了1000多张票。然后线程2被切换下去,线程1重新被调度。操作系统会先回复线程1的上下文数据。此时CPU拿到的tickets又变成了10000,线程1再进行抢票。此时就出现了CPU与内存中的数据错乱的问题。
我们再看tickets--。这也正是tickets能够变成负数的原因。当一个tickets==1时,发现能够抢票。当时刚执行完打印,也就是并没有抢票就被切换下去了,此时该线程也并没有保存tickets数据。同时其它线程被切换上来进行抢票后,票数变为0。再次恢复之前被切换下去的下线程,此时已经没票了,但是该线程以为还有票,于是当把tickets数据读到寄存器中后(tickets为0),进行计算--操作,再次写入内存票数就变成了负数。
上述讲解了两种出现错误的情况。针对上述例子,我们再来看一下临界资源和临界区:
tickets-- 操作并不是原子操作,而是对应三条汇编指令:
- load :将共享变量ticket从内存加载到寄存器中
- update : 更新寄存器里面的值,执行-1操作
- store :将新值,从寄存器写回共享变量ticket的内存地址
要解决以上问题,需要做到三点:
- 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
- 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
- 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。
要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量
2、2 互斥锁
什么是锁?在Linux操作系统中,锁(Lock)是一种同步机制,用于保护共享资源免受多个并发线程访问和修改的干扰。锁可以防止多个线程同时访问或修改共享资源,从而确保对共享资源的安全访问。
2、2、1 什么是互斥锁
当有一个线程申请互斥锁资源,也就是加锁成功后,其他线程就不会申请所资源成功。其他线程也就会进入阻塞等待状态。可以理解为把其他线程阻塞在外面了,不能有多个线程同时执行去申请互斥锁。这就是线程互斥。同一个线程不能执行互斥锁内的资源,同时只有一个线程能够执行。当进行对所资源释放,也就是解锁后,其他线程才可申请所资源。
2、2、2 pthread_mutex_t
pthread_mutex_t是一个用于实现线程同步的互斥量类型,也就是我们所说的互斥锁的类型。pthread_mutex_t可以用来保护共享资源,防止多个线程同时访问和修改数据而导致的竞争条件。使用pthread_mutex_t时,通常需要进行以下步骤:初始化、加锁、解锁、销毁。
下面对每个步骤进行详细解释,并提供一个示例代码:
初始化互斥锁: 可以使用pthread_mutex_init函数来初始化互斥锁。初始化后的互斥锁状态为未锁定状态。
示例代码:
pthread_mutex_t mutex; // 定义互斥锁变量// 初始化互斥锁 int result = pthread_mutex_init(&mutex, NULL); if (result != 0) {// 初始化失败的处理逻辑 }
加锁: 使用pthread_mutex_lock函数可以将互斥锁设置为锁定状态,如果互斥锁已被其他线程锁定,当前线程会进入阻塞状态,直到获取到锁。
示例代码:
// 加锁 int result = pthread_mutex_lock(&mutex); if (result != 0) {// 加锁失败的处理逻辑 }
解锁: 使用pthread_mutex_unlock函数可以将互斥锁设置为未锁定状态,释放锁供其他线程使用。
示例代码:
// 解锁 int result = pthread_mutex_unlock(&mutex); if (result != 0) {// 解锁失败的处理逻辑 }
销毁互斥锁: 在不再需要使用互斥锁时,可以使用pthread_mutex_destroy函数来销毁已初始化的互斥锁。
示例代码:
// 销毁互斥锁 int result = pthread_mutex_destroy(&mutex); if (result != 0) {// 销毁失败的处理逻辑 }
注意事项:
- pthread_mutex_t的初始化应该在使用前完成,并且应该确保互斥锁变量的作用域覆盖了所有对该锁的访问。
- 对于每个加锁操作,都应该有对应的解锁操作,以避免资源泄漏和死锁的情况发生。
- 建议在使用互斥锁时考虑锁的粒度,尽量保持锁的范围小并且锁的持有时间短,以提高并发性能。
后面也会对注意事项接进行解释。线面我们先来看一下对pthread_mutex_t 进行初始化处理操作的详解。其他操作简单,就不再解释。
2、2、3 pthread_mutex_init 初始化
pthread_mutex_init函数是一个用于初始化互斥锁的函数,它的原型如下:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
该函数主要用于将传入的mutex指向的互斥锁对象进行初始化,使其可以正确地使用。
参数说明:
mutex
:指向要初始化的互斥锁对象的指针。attr
:指向包含互斥锁属性的对象的指针。可以通过该参数来设置互斥锁的属性,如果为NULL,则使用默认属性。返回值:
- 若成功,返回0;
- 若失败,返回错误代码。
调用pthread_mutex_init函数会对互斥锁进行初始化,包括分配内存和设定默认属性。属性我们一般选择传入nullptr,就是默认属性。
当pthread_mutex_t 对象为全局时,我们也可选择不用 pthread_mutex_init 进行初始化。可直接使用宏:PTHREAD_MUTEX_INITIALIZER来初始化。代码如下:
pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;
2、3 抢票加互斥锁
我们对抢票的临界区加锁,这样就不会出现多个执行流去访问临界资源的情况了。
代码如下:
int tickets=10000; pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;void* getTickets(void* args) {while(true){pthread_mutex_lock(&mtx);if(tickets>0){usleep(1000);printf("%s: %d\n",(char*)args,tickets);tickets--;pthread_mutex_unlock(&mtx);}else{pthread_mutex_unlock(&mtx);break;}usleep(rand()%2000);}return nullptr; } int main( void ) {srand(time(nullptr));pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, getTickets, (void*)"thread 1");pthread_create(&t2, NULL, getTickets, (void*)"thread 2");pthread_create(&t3, NULL, getTickets, (void*)"thread 3");pthread_create(&t4, NULL, getTickets, (void*)"thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);return 0; }
我们再看运行结果:
上述代码是定义全局变量的互斥锁。我们再来看一下局部变量的互斥锁是怎么使用的。代码如下:
#define THREAD_NUM 5int tickets=10000;class ThreadData { public:ThreadData(const string& n,pthread_mutex_t* pm):tname(n),pmtx(pm){}public:string tname;pthread_mutex_t* pmtx; };void* getTickets(void* args) {ThreadData* data=(ThreadData*) args;while(true){pthread_mutex_lock(data->pmtx);if(tickets>0){usleep(1000);printf("%s: %d\n",data->tname.c_str(),tickets);tickets--;pthread_mutex_unlock(data->pmtx);}else{pthread_mutex_unlock(data->pmtx);break;}usleep(rand()%2000);}return nullptr; } int main( void ) {srand(time(nullptr));pthread_t t1, t2, t3, t4;pthread_mutex_t mtx;pthread_mutex_init(&mtx,nullptr);pthread_t t[THREAD_NUM];// 多线程抢票的逻辑for(int i = 0; i < THREAD_NUM; i++){std::string name = "thread ";name += std::to_string(i+1);ThreadData *td = new ThreadData(name, &mtx);pthread_create(t + i, nullptr, getTickets, (void*)td);}for(int i = 0; i < THREAD_NUM; i++){pthread_join(t[i], nullptr);}pthread_mutex_destroy(&mtx);return 0; }
2、4 互斥锁总结
加锁就是串行执行了吗?答案是是的。任何时候只允许申请锁成功的线程执行临界区代码。因为变为串行执行,所以我们要在加锁的时候,一定要保证加锁的粒度,越小越好!!!不必要的代码可放在锁外。
加锁了之后,线程在临界区中,是否会切换,会有问题吗?答案是会被切换的。但是并不会出现问题。虽然被切换了,但是你是持有锁被切换的,所以其他抢票线程要执行临界区代码,也必须先申请锁,锁它是无法申请成功的,所以,也不会让其他线程进入临界区,就保证了临界区中数据一致性!!!只要一个线程,且需要访问临界资源,就必须申请锁。
在没有持有锁的线程看来,对我最有意义的情况只有两种:1.线程1没有持有锁(什么都没做))2.线程1释放锁(做完),此时我可以申请锁!!!
要访问临界资源,每一个线程都必须现申请锁,每一个线程都必须先看到同一把锁且访问它,锁本身不就是一种共享资源吗!!!那么谁来保证锁的安全呢?所以,为了保证锁的安全,申请和释放锁,必须是原子性的。所以是由它自己自己保证。那么是怎么保证的呢?
三、互斥锁实现原理
经过上面的例子,大家已经意识到单纯的 i++ 或者 ++i 都不是原子的,有可能会有数据一致性问题。
为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。
具体可结合下图理解:
其实我们也不难发现,申请互斥锁资源的在底层本质就是一个交换指令。而交换的本质就是使其在内存中的共享资源变成了线程的私有资源!!!当交换后值为1时,就说明申请互斥锁资源成功。从头到尾,一直是在交换,一直是只有一个1。所以保证了只有一个线程能够申请互斥锁资源成功。
相关文章:
【Linux从入门到精通】多线程 | 线程互斥(互斥锁)
上篇文章我们对线程 | 线程介绍&线程控制介绍后,本篇文章将会对多线程中的线程互斥与互斥锁的概念进行详解。同时结合实际例子解释了可重入与不被重入函数、临界资源与临界区和原子性的概念。希望本篇文章会对你有所帮助。 文章目录 引入 一、重入与临界 1、1 可…...
Echarts 散点图的详细配置过程
文章目录 散点图 简介配置步骤简易示例 散点图 简介 Echarts散点图是一种常用的数据可视化图表类型,用于展示两个或多个维度的数据分布情况。散点图通过在坐标系中绘制数据点的位置来表示数据的关系。 Echarts散点图的特点如下: 二维数据展示ÿ…...
Nginx详解 五:反向代理
文章目录 1. 正向代理和反向代理1.1 正向代理概述1.1.1 什么是正向代理1.1.2 正向代理的作用1.1.3 正向代理的基本格式 1.2 反向代理概述1.2.1 什么是反向代理1.2.2 反向代理可实现的功能1.2.3 反向代理的可用模块 2. 配置反向代理2.1 反向代理配置参数2.1.1 proxy_pass2.1.2 其…...
【PDF密码】PDF文件打开之后不能打印,怎么解决?
正常的PDF文件是可以打印的,如果PDF文件打开之后发现文件不能打印,我们需要先查看一下自己的打印机是否能够正常运行,如果打印机是正常的,我们再查看一下,文件中的打印功能按钮是否是灰色的状态。 如果PDF中的大多数功…...
深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
前言:对于库函数有适当了解的朋友们,对于 qsort 函数想必是有认知的,因为他可以对任意数据类型进行排序的功能属实是有点厉害的,本次分享,笔者就给大家带来 qsort 函数的全面的解读 本次知识的分享笔者分为上下俩卷文章…...
Azure + React + ASP.NET Core 项目笔记一:项目环境搭建(二)
有意义的标题 pnpm 安装umi4 脚手架搭建打包语句变更Visual Studio调试Azure 设置变更发布 pnpm 安装 参考官网,或者直接使用npm安装 npm install -g pnpmumi4 脚手架搭建 我这里用的umi4,官网已附上 这里需要把clientapp清空,之后 cd Cl…...
Vmware通过VMware tools设置共享文件夹
步骤说明: 先安装VMware tools,再设置共享文件夹即可。 写在前面: 刚安装虚拟机时,窗口可能显得太小,这是窗口分辨率没有调整导致的。 点击设置->显示->分辨率调整即可 一、安装VMware tools 1.1 点击虚拟机…...
RPA机器人流程自动化专题培训大纲 (针对大学生的版本)
一、课程简介 RPA机器人流程自动化是一种新兴的技术,它通过软件机器人模拟人类操作计算机完成重复性任务,从而实现业务流程的自动化。本课程旨在介绍RPA机器人流程自动化的基本概念、原理和应用,并通过实践案例演示如何应用RPA机器人流程自动…...
数据在内存中的存储——练习4
题目: int main() {char a[1000];int i;for(i0; i<1000; i){a[i] -1-i;}printf("%d",strlen(a));return 0; }思路分析: 已知条件: 通过循环遍历,我们得到的结果是 -1、-2、-3、-4等等。这些是数组内部的存储的元…...
Python 06 之面向对象基础
😀前言 在日常编程和软件开发中,我们通常会遇到各种各样的问题,其中很多问题都可以通过面向对象的程序设计方法来解决。面向对象编程不仅可以使代码更加组织化和系统化,而且还可以提高代码的重用性和可维护性。 . 在本教程中&…...
去除pdf/word的水印艺术字
对于pdf中的水印如果无法去除水印,则先另存为word,然后再按下面办法处理即可: 查看宏,创建:删除艺术字 添加内容: Sub 删除艺术字()Dim sh As ShapeFor Each sh In ActiveDocument.ShapesIf sh.Type msoT…...
【Linux】使用 Alist 实现阿里云盘4K播放
一、安装 Alist 官方文档 默认安装在 /opt/alist 中 curl -fsSL "https://alist.nn.ci/v3.sh" | bash -s install自定义安装路径,将安装路径作为第二个参数添加,必须是绝对路径,如果路径以 alist 结尾,则直接安装到给定…...
Gof23设计模式之状态模式
1.概述 【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能…...
如何免费下载RunWayML产生的视频文件
问题: 首先没有下载的按钮。 其次如果直接“视频另存为”菜单,报错。 解决方案: 1)复制视频链接。 2)新开chrome,在url中粘贴上一步的url路径。 3)当看到视频后,在视频上面右键“…...
9.14 C++作业
仿照vector手动实现自己的myVector,最主要实现二倍扩容功能 #include <iostream>using namespace std;template <typename T> class Myvector {T *data; //存储数据的数组int len; //当前数组的长度int mycapa; //容纳数据的总容量public://…...
java关于文件记录篇章之文件夹创建篇
今天,创建一个文件夹目录的时候,创建多级目录的时候发现,自己老是创建失败,但是系统显示文件夹创建成功,但是你去找文件夹的时候,又发现创建失败,这里在我成功之后封装了一个创建文件夹的创建对…...
显示器显示的画面突然偏红色如何解决
显示器显示的画面突然偏红色如何解决 1. 概述2. 解决方法结束语 1. 概述 显示器显示的画面突然偏红色 ,使用向日葵远程电脑,看到的画面是正常的,但是显示器上的画面确还是骗红的,这时候就需要看一下是不是开启了系统也夜间模式&a…...
【element-ui】 el-table 表格动态合并相同数据单元格最全教程,可指定列+自定义合并条件,附完整代码
el-table合并单元格 1.固定合并 官方挺提供的合并具体某行列的方法:el-table合并行或列通过给table传入span-method方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row、当前列column、当前行号rowIndex、当前列号columnIndex四个属性。 该函数可以返回一个包含…...
管理方法论:6. 正视团队冲突——化解危机,长治久安
概念 团队冲突指的是两个或两个以上的团队在目标、利益、认识等方面互不相容或互相排斥,从而产生心理或行为上的矛盾,导致抵触、争执或攻击事件。 参考: https://baike.baidu.com/item/%E5%9B%A2%E9%98%9F%E5%86%B2%E7%AA%81/6747073 htt…...
基于SpringBoot的一套强大后台管理系统
概述 一个功能强大而完善的后台管理系统框架,用户可基于此框架进行二次开发,定制成符合自己的需求的后台管理系统! 详细 运行截图: 项目结构: 详细说明: 环境说明: jdk1.8mavenMySQL5.7 项…...
音乐项目后台管理系统出现的问题
1.当对歌手的歌曲进行编辑时候,会把所有的歌曲信息给修改了。 解决方法:修改controller层的中SongController代码中的这一行代码 boolean flag songService.updateById(song); 2.添加歌曲,在弹出框中输入,没有显示。原因:前端页…...
数据结构——图(图的存储及基本操作)
文章目录 前言一、邻接矩阵法(顺序存储)1.无向图存储邻接矩阵算法2.有向图存储邻接矩阵算法 二、邻接表法(图的链式存储结构)总结 前言 邻接矩阵法(图的顺序存储结构) 1.1 无向图邻接矩阵算法 1.2 有向图邻接矩阵算法邻接表法(图的一种链式存储结构) 一…...
2023年项目管理工具使用趋势分析及预测
随着技术的不断进步以及工作和领导态度的演变,各个行业都在经历着深刻的变革。项目管理领域同样如此,团队项目的技术和人员管理风格及策略正在不断地调整与优化,以适应新冠疫情后所呈现出的新的工作场所格局。在此背景下,以下是我…...
Vue3 实现一个无缝滚动组件(支持鼠标手动滚动)
Vue3 实现一个无缝滚动组件(支持鼠标手动滚动) 前言 在日常开发中,经常遇到需要支持列表循环滚动展示,特别是在数据化大屏开发中,无缝滚动使用频率更为频繁,在jquery时代,我们常用的无缝滚动组…...
【IP数据报】IP地址和MAC地址的区别
1、用IP地址来标识Internet的主机 在每个IP数据报中,都会携带源IP地址和目标IP地址来标识该IP数据报的源和目的主机。IP数据报在传输过程中,每个中间节点(IP 网关)还需要为其选择从源主机到目的主机的合适的转发路径(即路由)。IP协议可以根据路由选择协…...
高并发笔记
如何设计一个高并发系统?:https://mp.weixin.qq.com/s/yFc-70DEhloWn0G3GDa6Yw 分布式 ID 服务实践:https://mp.weixin.qq.com/s/KAts9Zjj8JpEd0Q6pqLlgQ 一文聊透布隆过滤器:https://mp.weixin.qq.com/s/qJ2fDm1Z57bPSzOBrgiqfg …...
eNSP网络学习
一、eNSP 1.什么是eNSP eNSP(Enterprise Network Simulation Platform)是一款由华为提供的免费的、可扩展的、图形化操作的网络仿真工具平台,主要对企业网络路由器、交换机进行软件仿真,完美呈现真实设备实景,支持大型网络模拟,让…...
广州xx策划公司MongoDB恢复-2023.09.09
2023.09.08用户的MongoDB数据库被勒索病毒攻击,数据全部被清空。 提示: mongoDB的默认端口为27017,黑客通常通过全网段扫描27017是否开放判断是否是MongoDB服务器。一旦发现27017开放,黑客就会用空密码、弱密码尝试连接数据库。黑…...
golang --- module-aware 模式下 包引入
一、文件列表如下 其中helloWorld目录是main包(package)所在目录,即该目录下所有的goy源文件(不包含子目录)属于main包,hello.go是mian函数所在文件 二、module-aware 模式启用 开启mod模式 go env -w G…...
从原理到实践 | Pytorch tensor 张量花式操作
文章目录 1.张量形状与维度1.1标量(0维张量):1.2 向量(1维张量):1.3矩阵(2维张量):1.4高维张量: 2. 张量其他创建方式2.1 创建全零或全一张量:2.2…...
网站改版设计要多久/百度搜索关键词技巧
留学监理服务网东北大学计算机工程(本硕连读) - Computer Engineering基本信息东 北 大 学 - Northeastern 工程学院 - 电子与计算机工程所属学校 所在院系University 系计算机工程(本硕连读) -专业名称 学历层次 本科Computer Engineering工程与技术 计算机与信息科学授予学位…...
齐河网站建设/小说排行榜百度
高中信息技术教学中存在的问题及对策分析时间:2014-08-04栏目:高中信息技术教学中存在的问题及对策分析高中信息技术教学中存在的问题及对策分析文/谭方芳摘要:我国在教育信息化和基础教育方面不断发生重大改革,一系列的政策改革措…...
滨海做网站的价格/教你如何建立网站
没有区别 都是存在session里在jsp里面有内置session引用 直接用第二种方法就可以了...
网站的新闻模块怎么做/网站内容seo
I just dont wanna give them any more ammunition than they already have.---《老友记》 第一季 第一集 我只是不想,让他们有藉题发挥的机会。 名词 n. [U] 1. 弹药,军火The ammunition depot is heavily guarded. 弹药库戒备森严。 2. 【喻】"炮弹"(指…...
广东网页空间网站平台/什么平台可以免费打广告
如果想跟我一起讨论的话,就快加入我的知识星球吧。星球里有一千多位同样爱好安全技术的小伙伴一起交流! 常用工具: Nmap使用详解Sqlmap使用详解Metasploit Framework(MSF)的使用MSF中kiwi模块的使用MSF中mimikatz模块的使用Msfvenonm生成后门木马...
做软欧的网站/许昌网站推广公司
参数化测试让您可以重复运行相同的测试过程,每次都使用不同的数据值。在参数化测试中,这些数据值称为参数,由测试类的参数化属性表示。MATLAB 使用参数化属性为每个测试运行生成参数名称和值。 在大多数情况下,MATLAB 可以在加载测试类定义时确定参数化属性的值。因此,您可…...