Linux -- 线程互斥

一 线程互斥的概念

  大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互。多个线程并发的操作共享变量,会带来一些问题。

  例如下面我们模拟一个多线程抢票的程序。使用一个全局变量 ticket 表示票的数量,创建多个线程进行抢票,代码如下:

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<vector>using namespace std;#define NUM 5
int ticket = 100;class threadData
{
public:threadData(int number){threadname = "thread-" + to_string(number);}public:string threadname;
};void* getTicket(void* args)
{threadData *td = static_cast<threadData*>(args);		    while(1){if(ticket > 0){usleep(1000);     // 模拟抢票的时间printf("%s is running, get a ticket: %d\n", td->threadname.c_str(), ticket);ticket--;}else{break;}}return nullptr;
}int main(){vector<pthread_t> tids;vector<threadData*> thread_datas;for(int i = 1; i <= NUM; i++){threadData* td = new threadData(i);pthread_t tid;pthread_create(&tid, nullptr, getTicket, td);tids.push_back(tid);thread_datas.push_back(td);}for(auto e : tids){pthread_join(e, nullptr);}		for(auto td : thread_datas){delete td;}return 0;
}

我们运行起来之后,会看到线程抢到了负数的票! 

  为什么会出现这种情况呢?这种情况我们称为共享数据在无保护的情况下,被多线程并发访问,造成了数据不一致问题!所以对于一个全局变量进行多线程并发减减或者加加,不是安全的!下面我们来分析一下。

  首先需要对 ticket- -,先要将 ticket 读入到 CPU 的寄存器中,然后在 CPU 中要进行计算操作,最后再将 ticket 数据写回内存中。至此就完成了一次 ticket- -,所以上面三个步骤,都会对应每一个汇编语句。

  那么假设我们现在有两个线程,分别为线程1和线程2,在线程执行的代码间隙中,线程是随时有可能会被切换的!而线程在执行的时候,将共享数据加载到 CPU 寄存器的本质就是把数据的内容变成了自己上下文的内容!也就是以拷贝的方式给自己单独拿了一份!

  那么如果线程1刚好读取到内存中的数据,假设此时数据还是100,此时它要被切换了,那么它就要把自己上下文数据保存起来,而保存上下文的本质就是以拷贝的方式,给自己单独拿了一份!那么此时线程1就把100保存到自己的上下文中了。

   

  接下来线程2就开始抢票了,此时线程2在它的时间片内已经抢了99张票了!此时内存中只剩下一张票!

  那么当线程2切换后,线程1继续拿着它的上下文数据放回CPU中计算,注意,此时线程1中的 ticket 还是100,那么计算完后为99,再将 99 写回内存中!此时就导致了 ticket 的数据不一致问题!所以 ticket- - 操作是不安全的!也就是它不具备原子性!

  

  另外,我们不仅仅在对 ticket- -,这种叫做数值计算,而且还在对 ticket 做判断是否大于0,这个过程也是在对 ticket 计算,这种叫做逻辑运算!所以,假设当前 ticket 为1了,在判断期间,可能会有多个线程在进行判断!因为一个线程在判断的期间有可能会被切走!此时它们每一个线程的上下文中都认为 ticket 是1,所以会将 ticket 减到负数!而且判断完毕之后,ticket 就不会被用了,在计算 ticket- - 的时候要重新到内存中读取数据!

  那么这个问题要怎么解决呢?对于共享数据的访问,需要保证任何时候只有一个执行流访问,这就是互斥!所以我们需要通过互斥的方式来解决,也就是互斥锁!接下来我们就开始学习互斥锁。

二 互斥锁 

   在 Linux 中,pthread 库给我们提供了一种互斥锁解决上面多线程访问共享数据不一致的问题。

互斥锁,顾名思义,我们用一个锁将多个线程访问的代码锁住,使其只有一个线程有钥匙,也就是同时只让一个线程进行访问,进而实现了互斥。

接下来我i们认识一下互斥锁的相关接口:

2.1 锁的初始化

  pthread_mutex_init()

#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);

  该接口是对一把锁初始化。我们可以看到第一个参数的类型是 pthread_mutex_t,这是库给我们提供的一种数据类型,也可以理解为锁的类型!

  第一个参数是输入型参数,我们定义一个 pthread_mutex_t 类型的锁传入它的地址即可

  第二个参数代表这把锁的属性,我们也不管,设置为 nullptr 即可。

  其实,初始化一把锁有两种方式,以上是一种方式,下面还有一种方式是定义一把全局的锁,如果我们使用下面的方法定义了一把锁,就不需要使用上面的方式了;而且也不用释放这把锁了,但是释放也没有问题。其中定义全局锁是固定的,如下:

				pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

2.2 锁的释放

pthread_mutex_destroy()

 #include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex);

  该接口是释放一把锁。第一个参数和初始化时的第一个参数一样。注意如果我们使用 pthread_mutex_init() 的方式初始化一把锁,必须要使用 pthread_mutex_destroy() 进行释放;但是使用全局锁就可以不用释放。

2.3 加锁

  pthread_mutex_lock()

 #include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);

该接口就是对一把锁进行加锁(也就是对该锁进行使用,上锁)

2.4 解锁

 #include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);

该接口就是对一把锁进行解锁,也就是解除对资源的锁定。

2.5 使用示例与说明

  下面我们就使用上面抢票的代码进行加锁,对 ticket 加锁。根据我们以前的知识,这个 ticket 就变成了临界资源,而临界资源并不是全部代码都在访问,而是只有一小部分在访问,我们就把这一小部分的代码的区域称为临界区。

  其实加锁的本质是用时间来换取安全,加锁的表现就是线程对于临界区代码需要串行执行,也就是类似于排队,所以加锁的原则就是尽量要保证临界区代码越少越好!所以上述的代码中,临界区应该是对 ticket 进行访问的区域!如下代码:

首先我们在类中定义一把锁,方便每个线程都有自己的锁:

class threadLock
{//我们将锁的id和锁姓名封装成一个类
public:std::string _name; // 给程序员识别的pthread_mutex_t* _lock; //让不同的线程使用的锁
public:threadLock(int num,pthread_mutex_t* Lock):_lock(Lock){_name="Thread"+std::to_string(num);}
};

  接下来我们在主函数中定义一把锁,注意,这里定义的锁,是在 main() 函数的栈帧中的,也就是主线程中的,由于我们抢票的程序也在主函数中,所以这样定义不会有问题;最后在主函数返回前释放锁,代码如下:

 

int main(){pthread_mutex_t lock; //定义一把锁pthread_mutex_init(&lock,nullptr); //进行锁的初始化std::vector<pthread_t> tids; //线程编号数组 这两组是为了方便释放std::vector<threadLock*> thread_locks; //锁编号数组for(int i=0;i<=NUM;i++){ //循环创建线程,进行抢票threadLock* td=new threadLock(i,&lock); //创建一个新锁pthread_t tid; pthread_create(&tid, nullptr, getTicket, td);//创建线程 同时抢票//让线程和锁进入不同的数组tids.push_back(tid);thread_locks.push_back(td);}//循环释放for(auto e : tids){pthread_join(e, nullptr);}for(auto td : thread_locks){delete td;}//删除锁pthread_mutex_destroy(&lock);return 0;
}

接下来是抢票的程序:

void* getTicket(void* args)
{//获取锁名和idthreadLock *td = static_cast<threadLock*>(args);		    while(1){ //循环抢票//上锁 每一次我们将要访问下面这段代码时,除了当前线程,禁止其它线程访问pthread_mutex_lock(td->_lock);if(ticket > 0){//有票 usleep(1000);     // 模拟抢票的时间printf("%s is running, get a ticket: %d\n", td->_name.c_str(), ticket);ticket--;pthread_mutex_unlock(td->_lock); //关锁usleep(10);}else{ //无票 关锁 退出pthread_mutex_unlock(td->_lock);break;}//pthread_mutex_unlock(td->_lock); //在这里不如上面分两端写,两端写锁占用的资源更少}std::cout << td->_name.c_str() << "` quit!" << std::endl;return nullptr;
}

头文件及杂项:

#include<iostream>
#include<pthread.h>
#include<string>
#include<vector>
#include<time.h>
#include<unistd.h>const int NUM=5; //创建的线程数量
int ticket=10000; //票数

 

  如上代码,在加锁和解锁锁的区域中,就称为临界区。其中,执行流在申请锁的时候,如果申请锁成功,才能往后执行后面的代码,如果不成功,就会阻塞等待!

  执行的结果如下:

我们可以看到,ticket 不会出现 0 和负数的情况了,也就是说,临界资源被并发访问导致数据不一致问题已经解决了!

但是代码中有些细节我们还需要讲解一下。

  1. 在抢票的程序中,我们可以看到,在一个线程抢完票后,解锁后,我们在其后面加了一句 usleep(10);,这是什么意思呢?很简单,当一个线程加锁后,其它线程就被阻塞等待挂起了,那么当该线程解锁时,其它线程还没来得及从阻塞状态转为运行状态,该线程又去申请锁了,也就是说,唤醒线程的成本更大;而且,我们抢完票后还有后续的代码需要执行,比如处理票的后续动作,这里我们就没有实现。也就是说,所以我们在一个线程解锁后,加上短暂的休眠时间,一是为了有时间唤醒其它线程,二是为了模拟抢票后的后续动作。
  2. 如果我们没有加上 usleep(10); 这句代码,那么该线程就会一直占用这把锁,所以导致票就被它抢完了。那么也就是说,这种纯互斥环境,如果锁分配不够合理,容易导致其它线程一直没有等待到资源,进程一直等待,一直在消耗,直到饿死,这就是饥饿问题。但是不是说只要有互斥,必有饥饿,而是适合纯互斥的场景,就用互斥!
  3. 新来的线程,必须要从等待队列的最后开始排队;解锁的线程,不能马上重新申请锁,必须也要从等待队列的最后开始排队。这就可以让所有的线程获取钥匙,按照一定的顺序,这种按照一定顺序性获取资源的称为同步,这个我们后面详谈。
  4. 每一个线程进入临界区访问临界资源的时候,首先需要申请加锁,所以锁本身就是共享资源,也就是临界资源!所以申请加锁和解锁本身就被设计为原子性的操作了!如何做到的呢?我们后面讲原理再谈。
  5. 那么在临界区中,线程可以被切换吗?可以切换!因为在线程被切出去的时候,是持有锁被切走的,所以在该线程被切换的时候,其他线程也不能进临界区访问临界资源,因为锁只有一把!所以对于其它线程来说,一个线程要么没有锁,要么释放锁,当前线程访问临界区的过程,对于其它线程是原子的!

三 锁的原理

  我们已经知道,ticket>0 不是原子的,因为这个操作会被分为三个汇编语句,那么什么是原子的呢?在计算机底层,我们认为,一条汇编语句就是原子的!

  为了实现互斥锁操作,大多数体系结构都提供了 swapexchange 指令,该指令的作用是把寄存器和内存单元的数据交换,由于只有一条汇编指令,保证了原子性。现在我们把 lockunlock 的伪代码演示一下:

  首先 movb 就是把 0 写入 al 寄存器,可以理解为 eax 寄存器:

  接下来 xchgb 就是将 al 寄存器中的内容和内存中定义的一个变量 mutex 进行交换,其中 mutex 的初始值为1:

接下来对 al 寄存器中的值进行判断,如果大于 0,说明申请加锁成功,否则申请加锁失败,挂起等待。

上面我们演示的都是一个线程来申请加锁,如果有两个线程来申请加锁呢?例如,线程1和线程2来申请加锁,而加锁的语句是一句,但是它被分为上面多个汇编语句,所以当一个线程执行到某一个汇编语句的时候,随时都有可能被切换!

假设线程1申请加锁的过程中,刚刚执行完第一步,即将 0 写入了 al 寄存器中,实际上是写入线程的硬件的上下文中。此时线程2来了,线程1要被切走,所以线程1将 al 寄存器中的内容保存起来,即将 0 保存起来,当切换回来的时候执行 xchgb 语句。  

线程2来的时候,再次将0写入 al 寄存器中,然后执行xchgb语句,将 al 寄存器中的内容和内存的内容交换,交换完成后,al 寄存器中的内容变成1,线程2中的上下文内容也变成1,正常来说线程2此时做判断,此时al寄存器的值大于0,所以可以直接返回。

但是如果在线程2做判断的时候,线程2需要被切走,线程1切回来,首先先要将上下文恢复回来,此时将 al 寄存器中的内容恢复成为0,然后和内存中的值交换,交换完后发现 al 寄存器中的值为 0,此时线程1就被挂起等待了。 

线程1被挂起等待后不会被调度,所以此时线程2被切回来,恢复上下文,把1放回al寄存器中,然后做判断,大于0,申请加锁成功,返回0。

所以从上面的过程我们可以看出,其实 xchgb 的语句最重要。交换的本质就是把内存中的数据,交换到CPU的寄存器中,也就是将数据交换到线程的上下文中!而线程上下文是线程私有的!另外,内存中的数据是被所有线程共享的,而锁只有一把,所以申请加锁的本质就是把一把共享的锁,让一个线程以一条汇编的方式,交换到自己的上下文中,就代表当前线程持有锁了!

那么解锁的汇编语句如下:

 

  其实就是将内存中的数据重置为1即可。并没有将上一次线程申请加锁的 1 交换回内存中,因为并不需要,因为每一个线程在申请加锁的时候首先需要将 0 写入 al 寄存器中,也就是写入自己的硬件上下文中,此时就相当于将原来申请过加锁的 1 覆盖掉。 

四、可重入和线程安全

4.1 概念

  • 线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题;
  • 重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。

也就是说,如果一个函数不可重入,那么在多线程执行时,可能会出现线程安全问题。如果一个函数可被重入的,那么就一定不会出现线程安全问题。

4.2 可重入与线程安全联系

  • 函数是可重入的,那就是线程安全的;
  • 函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题,如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

4.3 可重入与线程安全区别

  • 可重入函数是线程安全函数的一种;
  • 线程安全不一定是可重入的,而可重入函数则一定是线程安全的;
  • 如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。

最后总结就四个结论:

  • 线程安全描述的是并发的问题
  • 可重入描述的是函数特点的问题
  • 不可重入函数在多线程访问时,可能会出现线程安全问题
  • 可重入函数在多线程访问时,不会有线程安全问题

五、死锁

5.1.死锁概念

死锁是指在一组执行流中的一个线程持有一把锁,另一个线程持有另一把锁,但因互相申请对方的锁,并不释放自己的锁而处于的一种永久等待状态。

5.2 死锁的必要条件

首先我们了解一下什么叫做死锁的必要条件,也就是只要产生了死锁,必定所有的条件都要满足。也就是以下四个条件都要满足:

  • 互斥条件:一个资源每次只能被一个执行流使用
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系

5. 3. 避免死锁

  • 破坏死锁的四个必要条件之一
  • 加锁顺序一致
  • 避免锁未释放的场景
  • 资源一次性分配

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/275069.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

淘宝基于Nginx二次开发的Tengine服务器

最近在群里看到这样一张阿里云网关报错的截图&#xff0c;我保存下来看了下 看到下面有 Tengine提供技术支持&#xff0c;这个Tengine是什么东西呢&#xff1f;我搜索了下似乎是淘宝在nginx的基础上自己改的Web服务器 Tengine还支持OpenResty框架&#xff0c;该框架是基于Ngin…

ARM中专用指令(异常向量表、异常源、异常返回等)

状态寄存器传送指令 CPSR寄存器 状态寄存器传送指令:访问&#xff08;读写&#xff09;CPSR寄存器 读CPSR MRS R1, CPSR R1 CPSR 写CPSR MSR CPSR, #0x10 0x10为User模式&#xff0c;且开启IRQ和FRQ CPSR 0x10 在USER模式下不能随意修改CPSR&#xff0c;因为USER模式…

BUUCTF-----[CISCN 2019 初赛]Love Math

<?php error_reporting(0); //听说你很喜欢数学&#xff0c;不知道你是否爱它胜过爱flag if(!isset($_GET[c])){show_source(__FILE__); }else{//例子 c20-1$content $_GET[c];if (strlen($content) > 80) {die("太长了不会算");}$blacklist [ , \t, \r, \n…

【Android】工厂模式中 字体大小/显示重叠/显示不完整 相关 问题分析与解决

工厂模式中 字体大小/显示重叠/显示不完整 相关 问题分析与解决 1-Factory Mode是什么&#xff1f;2-Factory Mode的显示界面3-找到factory模块中对应设置字体尺寸的代码4-分析与修改代码 Tips 1-Factory Mode是什么&#xff1f; 在Android手机中&#xff0c;Factory Mode&…

Seata 2.x 系列【8】Spring Cloud 集成客户端

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Seata 版本 2.0.0 本系列Spring Boot 版本 3.2.0 本系列Spring Cloud 版本 2023.0.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 前言2. 问题演…

零基础如何学习Web 安全,如何让普通人快速入门网络安全?

前言 网络安全现在是朝阳行业&#xff0c;缺口是很大。不过网络安全行业就是需要技术很多的人达不到企业要求才导致人才缺口大 初级的现在有很多的运维人员转网络安全&#xff0c;初级也会慢慢的卷起来&#xff0c;但是岗位多不用怕&#xff0c;以后各大厂也都会要网络安全人…

读西游记第一回:西游记世界格局

天地之数&#xff1a; 元&#xff1a;十二万九千六百岁&#xff08;129600年&#xff09; 1元12会&#xff1a;子、丑、寅、卯、巳、午、未、申、酉、戌、亥。每会18000年。与12地支对应。 亥会期&#xff1a;前5400年混沌期&#xff0c;后5400年&#xff0c;盘古开天辟地&am…

YOLOv9改进项目|关于本周更新计划的说明24/3/12

目前售价售价59.9&#xff0c;改进点30个 专栏地址&#xff1a; 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 日期&#xff1a;24/3/12 本周更新计划说明&#xff1a; 1. 更新华为Gold YOLO中的…

【nodejs】“__dirname is not defined”错误修复

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ 原理CommonJS vs ESM错误原因 2️⃣ 禁用 ESM 模式并改用 CommonJS方案一&#xff1a;项目方案二&#xff1a;单文件 3️⃣ 在 ESM 模式下自实现__dirname&#x1f4d6; 参考资料 &#x1f6eb; 问题 描述 从网上找了一份代码&am…

链表基础知识详解(非常详细简单易懂)

概述&#xff1a; 链表作为 C 语言中一种基础的数据结构&#xff0c;在平时写程序的时候用的并不多&#xff0c;但在操作系统里面使用的非常多。不管是RTOS还是Linux等使用非常广泛&#xff0c;所以必须要搞懂链表&#xff0c;链表分为单向链表和双向链表&#xff0c;单向链表很…

【四】【算法分析与设计】贪心算法的初见

455. 分发饼干 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且每块饼干 j&#xff0c;都有…

提高螺栓连接强度——SunTorque智能扭矩系统

螺栓连接是工程中常见的一种连接方式&#xff0c;其强度对于设备的稳定性和安全性具有至关重要的影响。然而&#xff0c;由于各种因素的影响&#xff0c;螺栓连接在使用过程中往往会出现松动、断裂等问题&#xff0c;导致设备故障和安全隐患。因此&#xff0c;提高螺栓连接的强…

Kanebo HITECLOTH 高科技擦镜布介绍

Kanebo HITECLOTH&#xff0c;这款由日本KBSeiren公司制造的高科技擦镜布&#xff0c;以其卓越的清洁能力和超柔软的布质&#xff0c;成为了市场上备受瞩目的产品。 材质与特性 HITECLOTH采用0.1旦尼尔特级高级微纤维制造&#xff0c;质地细致、坚韧、不起颗粒。这种纤维的特…

利用HubSpot出海CRM和人工智能技术提升出海业务的效率和效果

在当今数字化时代&#xff0c;智能化营销已经成为企业获取客户和扩大市场份额的关键策略。特别是对于出海业务而言&#xff0c;利用智能化营销技术来应对不同文化、语言和市场的挑战&#xff0c;已经成为企业竞争的关键优势。今天运营坛将带领大家探讨如何利用HubSpot CRM和人工…

网络流量监控软件AnaTraf:优化性能、排除故障的最佳选择

目录 导言 网络流量监控的重要性 AnaTraf网络万用表的功能与优势 网络故障排除与优化网络性能 结论 导言 在当今数字化时代&#xff0c;计算机网络已经成为企业和组织的核心基础设施。然而&#xff0c;网络流量的管理和监控对于确保网络性能的稳定和优化至关重要。本文将介…

GIS软件应用(二)

任务&#xff1a; 1. 正确划分渔网并裁剪出研究区域 2. 渔网与poi数据正确空间链接并统计网格内类别POI数量 步骤&#xff1a; 将南京市边界进行投影变换&#xff0c;具体看我的这篇文章&#xff1a;GIS软件应用&#xff08;一&#xff09;-CSDN博客 选择ArcToolbox中的 Cr…

java八股文 笔记(持续更新中~)

1 Redis 2Mysql 3JVM 4java基础底层 5 spring 6 微服务 7.......(持续更新) One:Redis篇 1.穿透 2&#xff1a;击穿 3&#xff1a;雪崩 3 33 4:双写一致 5.持久化 2 JVM: 2&#xff1a; 3&#xff1a; 4&#xff1a; 5&#xff1a; 6&#xff1a; 7&#xff…

【Scrapy】京东商品数据可视化

【Scrapy】京东商品数据可视化 文章目录 【Scrapy】京东商品数据可视化  &#x1f449;引言&#x1f48e;一、爬取数据&#xff1a;1.1 scrapy爬虫库简介&#xff1a;1.2 技术实现&#xff1a;1.2.1搭建框架结构1.2.2 分析网页结构 二、数据保存&#xff1a;三、数据读取以及…

企业计算机服务器中了eking勒索病毒怎么办?Eking勒索病毒解密工具流程

网络数据安全问题一直是众多企业关心的主要话题&#xff0c;网络在为企业提供便利的同时&#xff0c;也为企业数据安全带来未知的隐患。近日&#xff0c;云天数据恢复中心接到许多企业求助&#xff0c;企业的计算机服务器遭到了eking勒索病毒攻击导致企业计算机服务器系统瘫痪无…

JMeter使用记录

文章目录 概述从0创建一个测试场景线程组配置元件CSV Data Set ConfigHTTP信息头管理器HTTP Cookie管理器HTTP请求默认值 逻辑控制器简单控制器IF控制器循环控制器while控制器 取样器HTTP取样 前置/后置处理器BeanShell处理器JSR223处理器 监听器查看结果树聚合报告汇总报告 概…