3_并发编程可见性(volatile)之缓存锁内存屏障过程

并发编程可见性volatile

1.背景原来

从下面的程序可以知道main线程把stop修改成false,而在t1线程没有中没有读取到stop值为false,所以导致了t1线程不能够停止。

从而说明stop值在线程t1不可见,解决这个问题在stop变量上添加volatile即可(public static volatile boolean stop = true;)

public class VolatileDemo {
    public static boolean stop=true;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            //t1线程执行
            int i=0;
            while(!stop){
            i++;
            }
        });
        t1.start();
        
        //main主线程执行的
        System.out.println("begin start thread");
        Thread.sleep(1000);
        stop=false;
    }
}

// 结果:t1线程并没有按照期望的结果执行,而是一值执行

在这里插入图片描述

并发编程可见性volatile

1.背景原来

从下面的程序可以知道main线程把stop修改成false,而在t1线程没有中没有读取到stop值为false,所以导致了t1线程不能够停止。

从而说明stop值在线程t1不可见,解决这个问题在stop变量上添加volatile即可(public static volatile boolean stop = true;)

public class VolatileDemo {
    public static boolean stop=true;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            //t1线程执行
            int i=0;
            while(!stop){
            i++;
            }
        });
        t1.start();
        
        //main主线程执行的
        System.out.println("begin start thread");
        Thread.sleep(1000);
        stop=false;
    }
}

// 结果:t1线程并没有按照期望的结果执行,而是一值执行

在这里插入图片描述

在整个计算机的发展历程中,除了CPU、内存以及I/O设备不断迭代升级来提升计算机处理性能之外,还有一个非常核心的矛盾点,就是这三者在处理速度的差异。如果内存和磁盘的处理性能没有跟上,就意味着整体的计算效率取决于最慢的设备,为了平衡这三者之间的速度差异,最大化的利用CPU。所以在硬件层面、操作系统层面、编译器层面做出了很多的优化。

  1. CPU增加了高速缓存
  2. 操作系统增加了进程、线程。通过CPU的时间片切换最大化的提升CPU的使用率
  3. 编译器的指令优化,更合理的去利用好CPU的高速缓存

2.基本使用

public class VolatileExample {
    public volatile static boolean stop=true;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            int i=0;
            while(stop){ 
                i++;
            }
        });
        t1.start();
        System.out.println("begin start thread");
        Thread.sleep(1000);
        stop=false;
    }
}

// 结果(程序可以正常结束):begin start thread

3.注意事项

4.底层原理

4.1 高速缓存

这个缓存行可以缓存存储在内存中的数据,CPU每次会先从缓存行中读取需要运算的数据,如果缓存行中不存在该数据,才会从内存中加载,通过
这样一个机制可以减少CPU和内存的交互开销从而提升CPU的利用率。cpu的缓存行(cache)分为L1、L2、L3总共3级, 其中L3缓存是共享.

在这里插入图片描述

4.2 缓存行

cup缓存是由缓存行主成的,缓存行是cup中最小单元。

在这里插入图片描述

伪共享问题?

下图:内存的xyz数据被cpu一次加载到一个缓存行中,这时线程core0读取缓存行中的X值,core1读取缓存行中Y值,正好X和Y值正好在一个缓存行中。就会2个线程存在竞争,当core0线程抢到,就会是core1缓存失效。这样来回就会影响性能。xyz中的缓存一个缓存行中,就存在伪共享,缓存行就会失效。所以缓存行中的数据,使用对齐填充来解决。

image-20240105204118813
对齐填充:(解决缓存行伪共享)

对齐填充是以空间换时间,使数据存在一个缓存行中,不足使用对齐填充。这样cup之间就不存在抢一个缓存行的数据,导致cup之间数据来回失效,从而提供了cup性能。

java8中使用 @Contended注解,来实现缓存填充。jvm需要参数:-XX:RestrictContended

4.3 缓存一致性

有了高速缓存后,就会出现出现缓存一致性问题,如下图:flag=false是初始值,当cup0和cpu1同时读取缓存值后,cpu0修改为flag为true,这时cpu1中的false不知道什么时候能读取到falg为true,这时就会出现缓存一致问题?

在这里插入图片描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解决缓存一致性引入

总线锁:【加载上面图的Bus上,这样就大大降低的cpu性能】

缓存锁:【x=20加缓存锁】

缓存一致性协议(MESI,MOSI)

MESI 有4种状态 [m修改缓存 i失效缓存 e独占缓存 s共享缓存 ]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

cpu缓存中有一个Snoopy嗅探器,来监视缓存的状态,并通知其他cup。

image-20240105211650188

cup访问–> 缓存-> 缓存锁,总线锁 --> 主存

volatie --> #lock --> 缓存锁,总线锁

image-20240105212318540

4.4 cup指令重排序

public class SeqExample {
    private volatile static int x=0,y=0;
    private volatile static int a=0,b=0;

    public static void main(String[] args) throws InterruptedException {
        int i=0;
        for(;;){
            i++;
            x=0;y=0;
            a=0;b=0;
            Thread t1=new Thread(()->{
                a=1;
                x=b;

                //
                x=b;
                a=1;
            });
            Thread t2=new Thread(()->{
                b=1;
                y=a;
                //
                y=a;
                b=1;
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            String result="第"+i+"次("+x+","+y+")";
            if(x==0&&y==0){
                System.out.println(result);
                break;
            }else{

            }
        }
    }
}

// 结果:第3243次(0,0);
//分析:就会出现指令从排序
Thread t1=new Thread(()->{
    a=1;
    x=b;
    
    //变成下面执行顺序
    x=b;
    a=1;
});
Thread t2=new Thread(()->{
    b=1;
    y=a;
    
    //变成下面执行顺序
    y=a;
    b=1;
});
    

cpu如何导致指令重排序?

cpu0写数据到保存到内存,这里会议缓存失效,从而使用cpu阻塞状态或空闲状态。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.5 storebuffer

下图:cpu阻塞状态优化引入一个storebuffer, 异步处理。当cpu0读一个缓存数据后,发现缓存数据失效了,就放到storebuffer中。继续执行下面代码,直到cpu1执行这个为缓存数据为可以读到缓存数据,然后通知cpu0后面再把缓存失效数据处理写到内存中。–》这边就cpu就会出现cpu指令重排。

image-20240105214317880
int a =0;
function(){
    a=1;
    b=a+1;
    assert(b==2);//false 断言  
}

//false就出现,指令重排序
   b=a+1;
   a=1;


下图:过程图解

cpu0 把a=1放到storebuffer中,因为cpu1把a=1失效了

cpu0 把内存a=0读到独占缓存中

cpu0 把内存b=0读到独占缓存中

cup0 把b=a+1,b=1修改为写缓存中

cpu0收到cpu1[responese invalidat ack],把soter buffer中a=1读到 ,这个时候 a=1; b=1; 导致b==2为false

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.6 Store Forwarding

int a=0,b=0;
executeToCpu0(){
  a=1;
  b=1;
}
executeToCpu1(){
  while(b==1){
    assert(a==1);
  }
}

下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.9 失效队列

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1步骤中 cpu0写a=1时通知,

2步骤cpu1写b=1。

6步骤 读内存数据并放到cpu0共享缓存中

3步骤 cpu1中a缓存值失效,这时cpu1直接把值存到失效队列

4步骤 然后返回invalidate ack给cpu0

5步骤 cpu0把b=1变成可改缓存状态

7步骤 cpu1 b==1 为true

8步骤 cpu1 a=0 共享状态

最后才把失效队列中的a=0设置成失效缓存。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.8 内存屏障

#lock指令可以解决 : 缓存锁/总线锁和内存屏障的问题

CPU在性能优化道路上导致的顺序一致性问题,在CPU层面无法被解决,原因是CPU只是一个运算工
具,它只接收指令并且执行指令,并不清楚当前执行的整个逻辑中是否存在不能优化的问题,也就是说
硬件层面也无法优化这种顺序一致性带来的可见性问题。
因此,在CPU层面提供了写屏障、读屏障、全屏障这样的指令,在x86架构中,这三种指令分别是SFENCE、LFENCE、MFENCE指令,
sfence:也就是save fence,写屏障指令。在sfence指令前的写操作必须在sfence指令后的写操作前完成。
lfence:也就是load fence,读屏障指令。在lfence指令前的读操作必须在lfence指令后的读操作前完成。
mfence:也就是modify/mix,混合屏障指令,在mfence前得读写操作必须在mfence指令后的读写操作前完成。
在Linux系统中,将这三种指令分别封装成了, smp_wmb-写屏障 、 smp_rmb-读屏障 、 smp_mb-读写屏障 三个方法

4.9 cup性能优化的过程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.技术关联性

简单说一下volitale的认识

1.解决多线程下变量的可见性问题的。

2.解决过程中用到内存屏障 和 缓存锁和总线锁。

3.内存屏障是指在cup执行指令并处理指令过程中,不知道这些指令逻辑关系,只能有开发者知道,所以开发者在相关的地方可能发现指令重排序加上对于写屏障读屏障全屏障。

4.缓存锁有storebuffer 和 Store Forwarding ,总线锁是在cpu 高速缓存L3级缓存上加锁。

5.storebuffer 是在cup写缓存数据异步通知其他cup对于缓存值失效,并返回ack, 提高了cup的性能。

6.Store Forwarding是在storebuffer 是在cup写缓存数据异步通知其他cup对于缓存值失效,直接放到失效队列中。缩短了等待缓存值失效返回ack时间。

在整个计算机的发展历程中,除了CPU、内存以及I/O设备不断迭代升级来提升计算机处理性能之外,还有一个非常核心的矛盾点,就是这三者在处理速度的差异。如果内存和磁盘的处理性能没有跟上,就意味着整体的计算效率取决于最慢的设备,为了平衡这三者之间的速度差异,最大化的利用CPU。所以在硬件层面、操作系统层面、编译器层面做出了很多的优化。

  1. CPU增加了高速缓存
  2. 操作系统增加了进程、线程。通过CPU的时间片切换最大化的提升CPU的使用率
  3. 编译器的指令优化,更合理的去利用好CPU的高速缓存

2.基本使用

public class VolatileExample {
    public volatile static boolean stop=true;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            int i=0;
            while(stop){ 
                i++;
            }
        });
        t1.start();
        System.out.println("begin start thread");
        Thread.sleep(1000);
        stop=false;
    }
}

// 结果(程序可以正常结束):begin start thread

3.注意事项

4.底层原理

4.1 高速缓存

这个缓存行可以缓存存储在内存中的数据,CPU每次会先从缓存行中读取需要运算的数据,如果缓存行中不存在该数据,才会从内存中加载,通过
这样一个机制可以减少CPU和内存的交互开销从而提升CPU的利用率。cpu的缓存行(cache)分为L1、L2、L3总共3级, 其中L3缓存是共享.

image-20240105201313472

4.2 缓存行

cup缓存是由缓存行主成的,缓存行是cup中最小单元。

image-20240105203617124
伪共享问题?

下图:内存的xyz数据被cpu一次加载到一个缓存行中,这时线程core0读取缓存行中的X值,core1读取缓存行中Y值,正好X和Y值正好在一个缓存行中。就会2个线程存在竞争,当core0线程抢到,就会是core1缓存失效。这样来回就会影响性能。xyz中的缓存一个缓存行中,就存在伪共享,缓存行就会失效。所以缓存行中的数据,使用对齐填充来解决。

image-20240105204118813
对齐填充:(解决缓存行伪共享)

对齐填充是以空间换时间,使数据存在一个缓存行中,不足使用对齐填充。这样cup之间就不存在抢一个缓存行的数据,导致cup之间数据来回失效,从而提供了cup性能。

java8中使用 @Contended注解,来实现缓存填充。jvm需要参数:-XX:RestrictContended

4.3 缓存一致性

有了高速缓存后,就会出现出现缓存一致性问题,如下图:flag=false是初始值,当cup0和cpu1同时读取缓存值后,cpu0修改为flag为true,这时cpu1中的false不知道什么时候能读取到falg为true,这时就会出现缓存一致问题?

在这里插入图片描述

在这里插入图片描述

解决缓存一致性引入

总线锁:【加载上面图的Bus上,这样就大大降低的cpu性能】

缓存锁:【x=20加缓存锁】

缓存一致性协议(MESI,MOSI)

MESI 有4种状态 [m修改缓存 i失效缓存 e独占缓存 s共享缓存 ]

在这里插入图片描述

cpu缓存中有一个Snoopy嗅探器,来监视缓存的状态,并通知其他cup。

在这里插入图片描述

cup访问–> 缓存-> 缓存锁,总线锁 --> 主存

volatie --> #lock --> 缓存锁,总线锁

在这里插入图片描述

4.4 cup指令重排序

public class SeqExample {
    private volatile static int x=0,y=0;
    private volatile static int a=0,b=0;

    public static void main(String[] args) throws InterruptedException {
        int i=0;
        for(;;){
            i++;
            x=0;y=0;
            a=0;b=0;
            Thread t1=new Thread(()->{
                a=1;
                x=b;

                //
                x=b;
                a=1;
            });
            Thread t2=new Thread(()->{
                b=1;
                y=a;
                //
                y=a;
                b=1;
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            String result="第"+i+"次("+x+","+y+")";
            if(x==0&&y==0){
                System.out.println(result);
                break;
            }else{

            }
        }
    }
}

// 结果:第3243次(0,0);
//分析:就会出现指令从排序
Thread t1=new Thread(()->{
    a=1;
    x=b;
    
    //变成下面执行顺序
    x=b;
    a=1;
});
Thread t2=new Thread(()->{
    b=1;
    y=a;
    
    //变成下面执行顺序
    y=a;
    b=1;
});
    

cpu如何导致指令重排序?

cpu0写数据到保存到内存,这里会议缓存失效,从而使用cpu阻塞状态或空闲状态。

在这里插入图片描述

4.5 storebuffer

下图:cpu阻塞状态优化引入一个storebuffer, 异步处理。当cpu0读一个缓存数据后,发现缓存数据失效了,就放到storebuffer中。继续执行下面代码,直到cpu1执行这个为缓存数据为可以读到缓存数据,然后通知cpu0后面再把缓存失效数据处理写到内存中。–》这边就cpu就会出现cpu指令重排。

在这里插入图片描述

int a =0;
function(){
    a=1;
    b=a+1;
    assert(b==2);//false 断言  
}

//false就出现,指令重排序
   b=a+1;
   a=1;


下图:过程图解

cpu0 把a=1放到storebuffer中,因为cpu1把a=1失效了

cpu0 把内存a=0读到独占缓存中

cpu0 把内存b=0读到独占缓存中

cup0 把b=a+1,b=1修改为写缓存中

cpu0收到cpu1[responese invalidat ack],把soter buffer中a=1读到 ,这个时候 a=1; b=1; 导致b==2为false

在这里插入图片描述

4.6 Store Forwarding

int a=0,b=0;
executeToCpu0(){
  a=1;
  b=1;
}
executeToCpu1(){
  while(b==1){
    assert(a==1);
  }
}

下图:

在这里插入图片描述

4.9 失效队列

在这里插入图片描述

1步骤中 cpu0写a=1时通知,

2步骤cpu1写b=1。

6步骤 读内存数据并放到cpu0共享缓存中

3步骤 cpu1中a缓存值失效,这时cpu1直接把值存到失效队列

4步骤 然后返回invalidate ack给cpu0

5步骤 cpu0把b=1变成可改缓存状态

7步骤 cpu1 b==1 为true

8步骤 cpu1 a=0 共享状态

最后才把失效队列中的a=0设置成失效缓存。

在这里插入图片描述

4.8 内存屏障

#lock指令可以解决 : 缓存锁/总线锁和内存屏障的问题

CPU在性能优化道路上导致的顺序一致性问题,在CPU层面无法被解决,原因是CPU只是一个运算工
具,它只接收指令并且执行指令,并不清楚当前执行的整个逻辑中是否存在不能优化的问题,也就是说
硬件层面也无法优化这种顺序一致性带来的可见性问题。
因此,在CPU层面提供了写屏障、读屏障、全屏障这样的指令,在x86架构中,这三种指令分别是SFENCE、LFENCE、MFENCE指令,
sfence:也就是save fence,写屏障指令。在sfence指令前的写操作必须在sfence指令后的写操作前完成。
lfence:也就是load fence,读屏障指令。在lfence指令前的读操作必须在lfence指令后的读操作前完成。
mfence:也就是modify/mix,混合屏障指令,在mfence前得读写操作必须在mfence指令后的读写操作前完成。
在Linux系统中,将这三种指令分别封装成了, smp_wmb-写屏障 、 smp_rmb-读屏障 、 smp_mb-读写屏障 三个方法

4.9 cup性能优化的过程

在这里插入图片描述

5.技术关联性

简单说一下volitale的认识

1.解决多线程下变量的可见性问题的。

2.解决过程中用到内存屏障 和 缓存锁和总线锁。

3.内存屏障是指在cup执行指令并处理指令过程中,不知道这些指令逻辑关系,只能有开发者知道,所以开发者在相关的地方可能发现指令重排序加上对于写屏障读屏障全屏障。

4.缓存锁有storebuffer 和 Store Forwarding ,总线锁是在cpu 高速缓存L3级缓存上加锁。

5.storebuffer 是在cup写缓存数据异步通知其他cup对于缓存值失效,并返回ack, 提高了cup的性能。

6.Store Forwarding是在storebuffer 是在cup写缓存数据异步通知其他cup对于缓存值失效,直接放到失效队列中。缩短了等待缓存值失效返回ack时间。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java spring boot 获取resource目录下的文档

主要代码 String filePath"templates/test.xls" ClassPathResource classPathResource new ClassPathResource(filePath); InputStream inputStream classPathResource.getInputStream();目录 主要目录存放再这 代码案例 public void downloadTemplate( HttpS…

计算机毕业设计 基于SpringBoot的公司资产网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…

rhino犀牛怎么导入和调整背景图?

rhino犀牛怎么导入和调整背景图?Rhino建模过程中经常要用到背景图,为了更加方便快捷,我们会直接导入一些图片来当做背景,那么Rhino犀牛如何导入和调整背景图呢,让我们一起来看看吧 打开犀牛软件,进入操作界…

玩转Mysql 二(MySQL的目录结构与表结构)

一路走来,所有遇到的人,帮助过我的、伤害过我的都是朋友,没有一个是敌人。 一、MYSQL目录结构及命令存放路径 1、查看MYSQL数据文件存放路径 mysql> show variables like datadir; 注意:生成环境要提前规划好数据存放目录,存储一般以T为单位闪盘。 2、MYSQL命令存放…

Android两个APP之间跳转+手机图标修改

APP之间跳转 两个APP之间跳转同样使用Intent进行跳转,将需要跳转的APP都下载到手机中,通过主APP调用需要跳转的APP包名进行跳转。 最好在其中加上try-catch语句,当需要跳转的APP不存在时进行错误抓取。 代码如下: Intent mInten…

iview inputNumber有一个默认值1,来看解决方案

iview inputNumber为什么总有一个默认值1,怎么让它为空。 修改编辑没问题,赋值都没问题,但是新增的时候会有默认值1,也没赋值 这种情况你要手动解决,看看当前值有没有被覆盖 我这个问题就是出现覆盖导致的 看代码似乎…

生信技能33 - gnomAD数据库hg19/hg38 VCF文件批量下载脚本

gnomAD数据库下载地址 gnomAD downloads gnomAD v2.1.1数据集包含来自125,748个外显子组和15,708个全基因组的数据,所有这些数据都映射到GRCh 37/hg 19和GRCh 38/hg 38 两个版本的参考序列。 gnomAD数据库hg19与hg39 VCF文件批量下载脚本 download.sh # 获取当前目录路径…

STM32 IAP学习

STM32三种烧录方式 ISP:In System Programming(在系统编程) 执行芯片厂商的BootLoader程序进入ISP模式,进入ISP模式后,用户可选择官方提供的烧录通信接口(如:串口),并配…

机械配件移动商城课程概述

项目介绍 开发准备 任务 开源库介绍 框架搭建 工具类

单机部署Rancher

上次已经安装完毕了k8s了,但是想要界面化的管理,离不开界面工具,首推就是rancher,本文介绍安装rancher的安装,也可以将之前安装的k8s管理起来。 已经安装完毕docker和docker-ce的可以直接从第三部分开始。 一、基础准…

Rockchip平台双屏异显功能实现(基于Android13)

Rockchip平台双屏异显功能实现(基于Android13) 1. 异显实现方案 Rockchip SDK平台支持两种不同的异显方案:Android Presentation和Android Activity指定屏幕启动。 使用Android Presentation方案,需要在APP开发中调用相应接口以使指定视图&#xff08…

Debezium发布历史49

原文地址: https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/ 欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯. 使用发件箱模式进行可靠的微服务数…

FSMC驱动LCD

FSMC( Flexible static memory controller)全称“灵活的静态存储器控制器”,是 STM32中一个很有特色的外设,通过 FSMC,STM32可以通过FSMC与SRAM、ROM、PSRAM、Nor Flash和NandFlash存储器的引脚相连,从而进行数据的交换。 FSMC的本…

Java面试项目推荐,异构数据源数据流转服务DatalinkX

前言 作为一个年迈的夹娃练习生,每次到了春招秋招面试实习生时都能看到一批简历,十个简历里得有七八个是写商城或者外卖项目。 不由得想到了我大四那会,由于没有啥项目经验,又想借一个质量高点的项目通过简历初筛,就…

LeetCode 2221. 数组的三角和

文章目录 1. 题目 2. 解题 1. 题目 给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是 0 到 9 之间(两者都包含)的一个数字。 nums 的 三角和 是执行以下操作以后最后剩下元素的值: nums 初始包含 n 个元素。如果 n == 1 ,终止 …

系列十一、(三)Sentinel控制台

一、Sentinel控制台 二、实时监控 2.1、概述 实时监控,顾名思义是用来实时监控的,具体监控的是接口请求通过的QPS和拒绝的QPS,默认情况下没有访问记录,所以看不到任何记录,需要访问接口才会有记录。另外需要注意&…

每10分钟一更新的实时卫星影像

我们为大家分享了一个可以查看下载高时效卫星影像的方法。 这里再为大家推荐一个可以查看近乎实时的卫星影像的网站,卫星影像每10分钟更新一次。 实时卫星影像 打开网站(zoom.earth),可以查看实时卫星影像画面,在左…

[C#]C# OpenVINO部署yolov8图像分类模型

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 YOLOv8 抛弃了前几代模型的 Anchor-Base。 YOLO 是一种基于图像全局信息进行预测的目标检测系统。自 2015 年 Joseph Redmon、Ali Farhadi 等人提出初代模型以来,领域内的研究者们…

2024年天津体育学院专升本专业考试考生入场及考前须知

天津体育学院2024年高职升本科招生专业考试考生考前须知 一、考生入场及考试要求 1.考生于1月6日笔试考试当天,根据考试时间提前30分钟到达天津体育学院新校区东门,凭专业考试准考证、有效身份证原件,经查验合格后方可允许进入学校。 2.笔…

python语言在web上的应用:如何节省服务器资源?

背景介绍​ 在web开发中的应用广泛​ 在web开发中的应用广泛。随着互联网的发展,web应用越来越普遍,而Python作为一种简洁、高效的编程语言,被广泛应用于web开发领域。Python提供了丰富的库和框架,如Django、Flask等&#xff0c…