当前位置: 首页 > news >正文

【iOS】GCD

参考文章:GCD函数和队列原理探索

之前写项目的时候,进行耗时的网络请求使用GCD处理过异步请求,但对一些概念都很模糊,这次就来系统学习一下GCD相关


相关概念

什么是GCD?

Grand Center Dispatch简称GCD,是苹果公司开发的技术,以优化应用程序支持多核心处理器

GCD的优势:

  • GCD 是苹果公司为多核的并⾏运算提出的解决⽅案
  • GCD 会⾃动利⽤更多的CPU内核(⽐如双核、四核)
  • GCD 会⾃动管理线程的⽣命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉GCD想要执⾏什么任务,不需要编写任何线程管理代码

GCD要做的事情就是:GCD将任务添加到队列,并指定执行任务的函数

「任务」

任务: 执行操作,就是在线程中执行的那段代码。在GCD中是放在Block中的。执行任务有两种方式:

  • 同步执行(sync):
    • 必须等待当前语句执⾏完毕,才会执⾏下⼀条语句
    • 不会开启线程
    • 只能在当前线程中执行任务,不具备开启新线程的能力
  • 异步执行(async):
    • 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务
    • 可以在新的线程中执行任务,具备开启新线程的能力
    • 异步是多线程的代名词

注意:异步执行(async) 虽然具有开启新线程的能力,但是并不一定开启新线程,这跟任务指定的队列类型有关

两者主要的区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力

「队列」

队列(Dispatch Queue): 这里的队列指执行任务的等待队列,即用来存放任务的队列。每读取一个任务,则从队列中释放一个任务,参考下图:

请添加图片描述

队列分为两种:串行队列和并发队列。不同的队列中,任务排列的方式是不一样的,任务通过队列的调度,由线程池安排的线程来执行
两者都符合FIFO(先进先出)的原则:

  • 串行队列(Serial Dispatch Queue):每次只有一个任务被执行,让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
  • 并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)

注意:并发队列的并发功能只有在异步(dispatch_async)方法下才有效

两者的主要区别是:执行顺序不同,以及开启线程数不同。参考下图:

请添加图片描述

创建队列:

// 串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);// 并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_CONCURRENT);

MRC下,生成的队列需由程序员负责持有或释放,通过以下两个函数的引用计数来管理内存:

dispatch_retain(serialQueue);
dispatch_release(serialQueue);

队列与函数组合

队列用来调用任务,函数用来执行任务,那么队列和函数不同的配合会有怎样的运行效果呢?

  • 同步函数串行队列
    1. 不会开启线程,在当前线程中执行任务
    2. 任务串行执行,任务一个接着一个执行
    3. 会产生阻塞
  • 同步函数并发队列
    1. 不会开启线程,在当前线程中执行任务
    2. 任务一个接着一个执行
  • 异步函数串行队列
    1. 会开启一个线程
    2. 任务一个接着一个执行
  • 异步函数并发队列
    1. 开启线程,在当前线程执行任务
    2. 任务异步执行,没有顺序,CPU调度有关

GCD中的队列

主队列

  1. The main queue:系统自带的一个队列,放到这个队列中的代码会被系统分配到主线程中执行。Main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列, 交至其中的任务顺序执行(一个任务执行完毕后,再执行下一个任务)

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    

全局队列

  1. Global queues:整个应用程序存在4个全局队列(系统已经创建好,只需获得即可):高、中(默认)、后台三个优先级队列,可以调用dispatch_get_global_queue函数传入有下级来访问队列。全局队列是并行队列,可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务),并发功能只有在异步(dispatch_async)函数下才有效

    dispatch_queue_t globalQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_queue_t globalQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_queue_t globalQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    

自定义队列

  1. 用户自己创建队列:dispatch_queue_create创建的队列可以是串行的,也可以是并行的,因为系统已经给我们供了并行、串行队列,所以一般情况下我们不再需要在创建自己的队列。用户创建的队列可以有任意多个

注意: 分线程不能刷新UI,刷新UI只能在主线程。如果每个线程都刷新UI,将会很容易造成UI冲突,会出现不同步的情况,所以只有主线程中能刷新UI系统是为了降低编程的复杂程度,最大程度的避免冲突

相关案例分析

耗时性

任务是耗时的,不同函数只要执行任务,都会耗时

异步函数会开启线程,执行的耗时相对较少,在实际开发中,异步可以用来解决并发、多线程等问题

六种情况示例

主队列添加同步任务

在当前的main队列中添加一个任务,并同步执行该任务:

void mainSyncTest(void) {/*主队列同步不会开启线程会崩溃!*/NSLog(@"start");// dispatch_queue_t concurrentQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);dispatch_sync(serialQueue, ^{NSLog(@"a");});NSLog(@"b");
}

在当前流程中,默认队列就是主队列,也是一个串行队列,任务执行顺序应为:

在这里插入图片描述

而到了第二步的块任务,会向当前的主队列中添加一个任务NSLog(@"a");,因为主队列是一个串行队列,现在要执行b,必须要等a(任务块)执行完成,而a又必须等b执行完成,产生了相互等待问题,造成了死锁!见下图:

在这里插入图片描述

运行结果就是崩溃:

在这里插入图片描述

解决办法就是将主队列改成自定义的串行队列或并发队列:

NSLog(@"start");// dispatch_queue_t concurrentQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{NSLog(@"a");
});NSLog(@"b");

运行结果:

在这里插入图片描述

主队列添加异步任务
void mainAyncTest(void) {NSLog(@"start");dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);dispatch_async(serialQueue, ^{NSLog(@"a");});NSLog(@"b");
}

主队列添加异步任务不会阻塞,不会崩溃

运行结果:

在这里插入图片描述

并发队列添加异步任务
void concurrentAsyncTest(void) {dispatch_queue_t concurrentQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_CONCURRENT);NSLog(@"1 --- %@", [NSThread currentThread]);dispatch_async(concurrentQueue, ^{NSLog(@"2 --- %@", [NSThread currentThread]);dispatch_async(concurrentQueue, ^{NSLog(@"3 --- %@", [NSThread currentThread]);});NSLog(@"4 --- %@", [NSThread currentThread]);});NSLog(@"5 --- %@", [NSThread currentThread]);
}

并发队列,通道比较宽,不会导致任务的阻塞
每个任务复杂度基本一致,异步不会堵塞主线程,dispatch_async会开启一个新的线程去执行其中的任务块

运行结果:

在这里插入图片描述

并发队列添加同步任务
void concurrentSyncTest(void) {dispatch_queue_t concurrentQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_CONCURRENT);NSLog(@"1 --- %@", [NSThread currentThread]);dispatch_sync(concurrentQueue, ^{NSLog(@"2 --- %@", [NSThread currentThread]);dispatch_sync(concurrentQueue, ^{NSLog(@"3 --- %@", [NSThread currentThread]);});NSLog(@"4 --- %@", [NSThread currentThread]);});NSLog(@"5 --- %@", [NSThread currentThread]);
}

因为并发队列,所以不会导致队列任务的阻塞,同时因为是同步执行,所以不会开启新的线程,按照顺序去执行流:

在这里插入图片描述

串行队列添加异步任务
void serialAsyncTest(void) {// 串行队列NSLog(@"start --- %@", [NSThread currentThread]);dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);for (int i = 0; i < 5; ++i) {dispatch_async(serialQueue, ^{NSLog(@"%d --- %@", i, [NSThread currentThread]);});}NSLog(@"hello queue");NSLog(@"end --- %@", [NSThread currentThread]);
}

运行结果:

在这里插入图片描述

串行队列添加异步任务,开启了一条新线程,但是任务还是串行,所以任务是一个一个执行
另一方面可以看出,所有任务是在打印的start和end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行

串行队列添加同步任务
void serialSyncTest(void) {NSLog(@"start --- %@", [NSThread currentThread]);dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);for (int i = 0; i < 5; ++i) {dispatch_sync(serialQueue, ^{NSLog(@"%d --- %@", i, [NSThread currentThread]);});}NSLog(@"hello queue");NSLog(@"end --- %@", [NSThread currentThread]);
}

运行结果:

在这里插入图片描述

串行队列同步执行任务,所有任务都是在主线程中执行的,并没有开启新的线程。而且由于串行队列,所以按顺序一个一个执行
同时我们还可以看到,所有任务都在打印的start和end之间,这说明任务是添加到队列中马上执行的

串行队列添加同步和异步任务混合

void serialSyncAndAsyncTest(void) {NSLog(@"start --- %@", [NSThread currentThread]);dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);dispatch_async(serialQueue, ^{NSLog(@"a --- %@", [NSThread currentThread]);dispatch_sync(serialQueue, ^{NSLog(@"b --- %@", [NSThread currentThread]);});NSLog(@"d --- %@", [NSThread currentThread]);});NSLog(@"hello queue");NSLog(@"end --- %@", [NSThread currentThread]);
}

在异步添加的线程中,情况类似主队列添加同步函数,b(任务块)和d相互等待了,导致死锁!

运行结果:

在这里插入图片描述

并发队列多任务

void concurrentSyncAndAsyncTest(void) {dispatch_queue_t concurrentQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrentQueue, ^{NSLog(@"1");});dispatch_async(concurrentQueue, ^{NSLog(@"2");});dispatch_sync(concurrentQueue, ^{NSLog(@"3");});NSLog(@"0");dispatch_async(concurrentQueue, ^{NSLog(@"7");});dispatch_async(concurrentQueue, ^{NSLog(@"8");});dispatch_async(concurrentQueue, ^{NSLog(@"9");});
}
// 3 0 1 2 7 9 8
// 3 0 1 7 9 8 2
// 3 0 1 2 7 8 9
// 3 1 0 2 7 8 9
// 3 2 1 0 7 8 9
// 3 1 2 0 7 9 8

结果分析:

  • 主队列共10个任务
  • 1、2、3的顺序不确定
  • 3、0是同步任务,所以3一定在0前面
  • 7、8、9一定在0后面

GCD线程间通信

在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯

void communicateAmongThread(void) {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{for (int i = 0; i < 6; ++i) {NSLog(@"1 --- %@", [NSThread currentThread]);}// 回到主线程dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"2 --- %@", [NSThread currentThread]);});});
}

运行结果:

在这里插入图片描述

可以看到在其他线程中先执行操作,执行完了之后回到主线程执行主线程的相应操作

GCD的栅栏方法

我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏

这是没添加栅栏函数之前的结果,顺序不确定:

在这里插入图片描述

void barrierFunc(void) {dispatch_queue_t queue = dispatch_queue_create("666", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{NSLog(@"1 --- %@", [NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"2 --- %@", [NSThread currentThread]);});dispatch_barrier_async(queue, ^{NSLog(@"----barrier-----%@", [NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"3 --- %@", [NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"4 --- %@", [NSThread currentThread]);});
}

栅栏函数可保证异步操作的执行顺序,这里保证了1或2先执行,3或4后执行:

在这里插入图片描述

GCD的延时方法

当我们需要延迟执行一段代码时,就需要用到GCD的dispatch_after方法

void delayExec(void) {NSLog(@"run -- 0");dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 3秒后异步执行这里的代码...NSLog(@"run -- 2");});
}

运行结果:

在这里插入图片描述

GCD的一次性代码(只执行一次)

我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once方法。使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

void onceExec(void) {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{// 只执行一次的代码(这里面是默认线程安全的)NSLog(@"Hhhhhhhh");});
}onceExec();
onceExec();

调用两次只会执行一次:

在这里插入图片描述

GCD的队列组

有时候我们会有这样的需求:分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的队列组。

  • 我们可以先把任务放到队列中,然后将队列放入队列组中
  • 调用队列组的dispatch_group_notify回到主线程执行操作
void queueGroup(void) {// GCD的队列组dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 执行1个耗时的异步操作int i = 0;while (i < 100) {NSLog(@"1");i++;}});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 执行1个耗时的异步操作int i = 0;while (i < 100) {NSLog(@"2");i++;}});dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的异步操作都执行完毕后,回到主线程...NSLog(@"3");});
}

运行结果:

在这里插入图片描述

dispatch_set_target_queue

这个函数有两个作用:

  1. 改变队列的优先级
  2. 防止多个串行队列的并发执行

改变队列的优先级

dispatch_queue_create函数生成的串行队列和并发队列,都使用 与默认优先级的Global Dispatch Queue 相同执行优先级 的线程

void serialBackgroundQueue(void) {// 需求:生成一个后台的串行队列dispatch_queue_t serialQueue = dispatch_queue_create("bySelf", DISPATCH_QUEUE_SERIAL);dispatch_queue_t globalQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);// 第1个参数:需要改变优先级的队列// 第2个参数:目标队列dispatch_set_target_queue(serialQueue, globalQueueBackground);
}

防止多个串行队列的并发执行

如果是将任务追加到3个串行队列中,那么这些任务就会并发执行。因为每个串行队列都会创建一个线程,这些线程会并发执行
如果将多个串行的queue使用dispatch_set_target_queue指定到了同一目标,那么这多个串行queue在目标queue上就是同步执行的,不再是并行执行
将串行队列加入指定优先级队列,会按照加入优先级队列的顺序依次执行串行队列

未加入优先级队列:

在这里插入图片描述

void abandonSerialQueuesToConcurrent(void) {dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);//    dispatch_set_target_queue(queue1, targetQueue);
//    dispatch_set_target_queue(queue2, targetQueue);
//    dispatch_set_target_queue(queue3, targetQueue);dispatch_async(queue1, ^{NSLog(@"1 in");[NSThread sleepForTimeInterval:3.f];NSLog(@"1 out");});dispatch_async(queue2, ^{NSLog(@"2 in");[NSThread sleepForTimeInterval:2.f];NSLog(@"2 out");});dispatch_async(queue3, ^{NSLog(@"3 in");[NSThread sleepForTimeInterval:1.f];NSLog(@"3 out");});
}

加入后:

在这里插入图片描述

dispatch_suspend/dispatch_resume

dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行

挂起指定队列:dispatch_suspend(serialQueue);
恢复指定队列:dispatchp_resume(serialQueue);

void suspendOrResumeQueue(void) {dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);//提交第一个block,延时5秒打印。dispatch_async(queue, ^{sleep(5);NSLog(@"After 5 seconds...");});//提交第二个block,也是延时5秒打印dispatch_async(queue, ^{sleep(5);NSLog(@"After 5 seconds again...");});//延时一秒NSLog(@"sleep 1 second...");sleep(1);//挂起队列NSLog(@"suspend...");dispatch_suspend(queue);//延时10秒NSLog(@"sleep 17 second...");sleep(17);//恢复队列NSLog(@"resume...");dispatch_resume(queue);
}

线程安全

为了保证线程安全,我们之前了解过互斥锁,在书上给了我们dispatch_semaphore方法,我们总结为三点:

  1. synchronized加锁,属于互斥锁,当有线程执行锁住的代码的时候,其他线程会进入休眠,需要唤醒后才能继续执行,性能较低

    - (void)synchronizedSecurity {dispatch_async(dispatch_get_global_queue(0, 0), ^{// 加锁保证block中执行完成才会执行其他的@synchronized (self) {NSLog(@"1开始");sleep(2);NSLog(@"1结束");}});dispatch_async(dispatch_get_global_queue(0, 0), ^{@synchronized (self) {NSLog(@"2开始");sleep(2);NSLog(@"2结束");}});
    }
    
  2. 信号量semaphore加锁,属于自旋锁,当有线程执行锁住的代码的时候,其他线程会进入死循环的等待,当解锁后会立即执行,性能较高
    提供了三种函数:

    • dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量(同时有几个线程可以执行,一般是1)

    • dispatch_semaphore_wait:可以使总信号量减1,当信号总量小于0时就会一直等待(阻塞所在线程),否则就可以正常执行,这个放在要执行的代码的前面。

    • dispatch_semaphore_signal:发送一个信号,让信号总量加1,代码执行完成之后使用,使其他线程可以继续执行

      void semaphoreSecurity(void) {dispatch_semaphore_t semalook = dispatch_semaphore_create(1);dispatch_async(dispatch_get_global_queue(0, 0), ^{dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);NSLog(@"1开始");sleep(2);NSLog(@"1结束");dispatch_semaphore_signal(semalook);});dispatch_async(dispatch_get_global_queue(0, 0), ^{dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);NSLog(@"2开始");sleep(2);NSLog(@"2结束");dispatch_semaphore_signal(semalook);});
      }
      
  3. NSLock

    void lockSecurity(void) {NSLock *lock = [[NSLock alloc]init];dispatch_async(dispatch_get_global_queue(0, 0), ^{[lock lock];NSLog(@"1开始");sleep(2);NSLog(@"1结束");[lock unlock];});dispatch_async(dispatch_get_global_queue(0, 0), ^{[lock lock];NSLog(@"2开始");sleep(2);NSLog(@"2结束");[lock unlock];});
    }
    

    运行结果:

    在这里插入图片描述

相关文章:

【iOS】GCD

参考文章&#xff1a;GCD函数和队列原理探索 之前写项目的时候&#xff0c;进行耗时的网络请求使用GCD处理过异步请求&#xff0c;但对一些概念都很模糊&#xff0c;这次就来系统学习一下GCD相关 相关概念 什么是GCD&#xff1f; Grand Center Dispatch简称GCD&#xff0c;是…...

C语言 | Leetcode C语言题解之第282题给表达式添加运算符

题目&#xff1a; 题解&#xff1a; #define MAX_COUNT 10000 // 解的个数足够大 #define NUM_COUNT 100 // 操作数的个数足够大 long long num[NUM_COUNT] {0};long long calc(char *a) { // 计算表达式a的值// 将数字和符号&#xff0c;入栈memset(num, 0, sizeof(num));in…...

如何使用 API list 极狐GitLab 容器镜像仓库中的 tag?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…...

粒子群算法PSO优化BP神经网络(PSO-BP)回归预测——Python和MATLAB实现

下面是一个使用Python实现的粒子群算法&#xff08;PSO&#xff09;优化反向传播神经网络&#xff08;BP&#xff09;的示例代码。 以下是具体的代码实现&#xff1a; python import numpy as np from sklearn.datasets import make_regression from sklearn.model_selection…...

React-router路由配置及跳转

1、V6对比V5的修改内容 1、API: useNavigate 代替了useHistory 。 2、废弃了Route组件的exact属性。 3、组件 <Routes>代替了<Switch> 4、组件NavLink中移除了 activeStyle activeClassName 属性。 2、安装依赖react-router-dom npm install react-router-dom…...

vue3【实战】可编辑的脱敏信息

<script lang"ts" setup> import { ref, onMounted } from "vue"; let real_name ref("朝阳");let name ref("");onMounted(() > {name.value des_name(real_name.value); });function focusing() {name.value real_name…...

S71200 - 笔记

1 S71200 0 ProfiNet - 2 PLC编程 01.如何零基础快速上手S7-1200_哔哩哔哩_bilibili 西门子S7-1200PLC编程设计学习视频&#xff0c;从入门开始讲解_哔哩哔哩_bilibili...

linux系统查历史cpu使用数据(使用sar 查询cpu和网络占用最近1个月历史数据)。

一 sar 指令介绍 在 Linux 系统中&#xff0c;sar 是 System Activity Reporter 的缩写&#xff0c;是一个用于收集、报告和保存系统活动信息的工具。它是 sysstat 软件包的一部分&#xff0c;提供了丰富的系统性能数据&#xff0c;包括 CPU、内存、网络、磁盘等使用情况&am…...

Edge浏览器加载ActiveX控件

背景介绍 新版Edge浏览器也是采用Chromium内核&#xff0c;虽然没有谷歌浏览器市场占有率高&#xff0c;但是依托微软操作系统的优势&#xff0c;Edge浏览器还是发展很强劲&#xff0c;占据着市场第二的位置。随着微软停止服务IE浏览器&#xff0c;曾经风光无限的IE浏览器页退出…...

BUG与测试用例设计

一.软件测试的生命周期 需求分析→测试计划→测试设计,测试开发→测试执行→测试评估→上线→运行维护 二.BUG 1.bug的概念 (1)当且仅当规格说明(需求文档)是存在的并且正确,程序与规格说明之间的不匹配才是错误. (2)当需求规格说明书没有提到的功能,判断标准以最终用户为准…...

怎么在使用select2时,覆盖layui的下拉框样式

目录 1.覆盖下拉框样式代码 2.自定义样式 3.样式使用 1.覆盖下拉框样式代码 .layui-form-select .layui-select-title {border: none !important; /* 去除边框 */background-color: transparent !important; /* 去除背景色 */display: none;/* 其他你想要覆盖的样式 */} 2.自…...

MacOSM1 配置Miniconda环境,并设置自启动

文章目录 设置环境变量设置自启动参考 设置环境变量 cd vim .zshrc输入一下内容 # 配置Conda CONDA_HOME/Users/hanliqiang/miniconda3 PATH$CONDA_HOME/bin:$PATH生效配置 source .zshrc设置自启动 conda init zsh.zshrc 文件中将会出现以下内容 # >>> conda i…...

poi库简单使用(java如何实现动态替换模板Word内容)

目录 Blue留言&#xff1a; Blue的推荐&#xff1a; 什么是poi库&#xff1f; 实现动态替换 第一步&#xff1a;依赖 第二步&#xff1a;实现word模板中替换文字 模板word&#xff1a; 通过以下代码&#xff1a;&#xff08;自己建一个类&#xff0c;随意取名&#xf…...

机器人开源调度系统OpenTcs6二开-车辆表定义

前面已经知道opentcs 需要车辆的模型结构数据&#xff0c;将里面的数据结构化&#xff0c;已表的形式生成&#xff0c;再找一个开源的基础框架项目对车辆进行增删改的管理 表结构&#xff1a; CREATE TABLE Vehicle (id INT AUTO_INCREMENT PRIMARY KEY COMMENT 唯一标识符,n…...

麦歌恩MT6521-第三代汽车磁性角度传感器芯片

磁性编码芯片 -在线编程角度位置IC 描述&#xff1a; MT6521是麦歌恩微电子推出的新一代基于水平霍尔及聚磁片(IMC)技术原理的磁性角度和位置检测传感器芯片。该芯片内部包含了两对互成90放置的水平霍尔阵列及聚磁片&#xff0c;能够根据不同的型号配置来实现对XY&#xff0…...

【数据结构】堆,优先级队列

目录 堆堆的性质大根堆的模拟实现接口实现构造方法建堆入堆判满删除判空获取堆顶元素 Java中的PriorityQueue实现的接口构造方法常用方法PriorityQueue注意事项 练习 堆 如果有一个集合K {k0&#xff0c;k1&#xff0c; k2&#xff0c;…&#xff0c;kn-1}&#xff0c;把它的…...

2024 暑假友谊赛 2

Tree Queries - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路:LCA好题&#xff0c;也有看见用时间戳写的&#xff0c;不是很明白. 用LCA非常好写。 要想到,给出k个节点&#xff0c;要确定一条路径&#xff0c;使得给出的k个点要么在路径上&#xff0c;要么和路径中某点的…...

c++ 线程

在 C 中&#xff0c;std::thread 构造函数可以用于将参数传递给线程。这里是一个基本的示例&#xff0c;展示了如何使用 std::thread 来传递参数&#xff1a; #include <iostream> #include <thread>// 定义一个被线程调用的函数 void threadFunc(int arg1, doubl…...

【SpringBoot】URL映射之consumes和produces匹配、params和header匹配

4.2.3 consumes和produces匹配 //处理request Content-Type为"application/json"类型的请求 RequestMapping(value"/Content",methodRequestMethod.POST,consumes"application/json") public String Consumes(RequestBody Map param){ return…...

vscode配置django环境并创建django项目(全图文操作)

文章目录 创建项目工作路径下载python插件&#xff1a;创建虚拟环境1. 命令方式创建2. 图文方式创建 在虚拟环境中安装Django创建Django项目安装Django插件选择虚拟环境 创建项目工作路径 输入 code . 下载python插件&#xff1a; 创建虚拟环境 1. 命令方式创建 切换在工作目…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...