【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);
队列与函数组合
队列用来调用任务,函数用来执行任务,那么队列和函数不同的配合会有怎样的运行效果呢?
- 同步函数串行队列
- 不会开启线程,在当前线程中执行任务
- 任务串行执行,任务一个接着一个执行
- 会产生阻塞
- 同步函数并发队列
- 不会开启线程,在当前线程中执行任务
- 任务一个接着一个执行
- 异步函数串行队列
- 会开启一个线程
- 任务一个接着一个执行
- 异步函数并发队列
- 开启线程,在当前线程执行任务
- 任务异步执行,没有顺序,CPU调度有关
GCD中的队列
主队列
-
The main queue:系统自带的一个队列,放到这个队列中的代码会被系统分配到主线程中执行。Main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列, 交至其中的任务顺序执行(一个任务执行完毕后,再执行下一个任务)
dispatch_queue_t mainQueue = dispatch_get_main_queue();
全局队列
-
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);
自定义队列
- 用户自己创建队列: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
这个函数有两个作用:
- 改变队列的优先级
- 防止多个串行队列的并发执行
改变队列的优先级
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方法,我们总结为三点:
-
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结束");}}); }
-
信号量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);}); }
-
-
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
参考文章:GCD函数和队列原理探索 之前写项目的时候,进行耗时的网络请求使用GCD处理过异步请求,但对一些概念都很模糊,这次就来系统学习一下GCD相关 相关概念 什么是GCD? Grand Center Dispatch简称GCD,是…...
C语言 | Leetcode C语言题解之第282题给表达式添加运算符
题目: 题解: #define MAX_COUNT 10000 // 解的个数足够大 #define NUM_COUNT 100 // 操作数的个数足够大 long long num[NUM_COUNT] {0};long long calc(char *a) { // 计算表达式a的值// 将数字和符号,入栈memset(num, 0, sizeof(num));in…...
如何使用 API list 极狐GitLab 容器镜像仓库中的 tag?
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab :https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署…...
粒子群算法PSO优化BP神经网络(PSO-BP)回归预测——Python和MATLAB实现
下面是一个使用Python实现的粒子群算法(PSO)优化反向传播神经网络(BP)的示例代码。 以下是具体的代码实现: 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编程设计学习视频,从入门开始讲解_哔哩哔哩_bilibili...
linux系统查历史cpu使用数据(使用sar 查询cpu和网络占用最近1个月历史数据)。
一 sar 指令介绍 在 Linux 系统中,sar 是 System Activity Reporter 的缩写,是一个用于收集、报告和保存系统活动信息的工具。它是 sysstat 软件包的一部分,提供了丰富的系统性能数据,包括 CPU、内存、网络、磁盘等使用情况&am…...
Edge浏览器加载ActiveX控件
背景介绍 新版Edge浏览器也是采用Chromium内核,虽然没有谷歌浏览器市场占有率高,但是依托微软操作系统的优势,Edge浏览器还是发展很强劲,占据着市场第二的位置。随着微软停止服务IE浏览器,曾经风光无限的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留言: Blue的推荐: 什么是poi库? 实现动态替换 第一步:依赖 第二步:实现word模板中替换文字 模板word: 通过以下代码:(自己建一个类,随意取名…...
机器人开源调度系统OpenTcs6二开-车辆表定义
前面已经知道opentcs 需要车辆的模型结构数据,将里面的数据结构化,已表的形式生成,再找一个开源的基础框架项目对车辆进行增删改的管理 表结构: CREATE TABLE Vehicle (id INT AUTO_INCREMENT PRIMARY KEY COMMENT 唯一标识符,n…...
麦歌恩MT6521-第三代汽车磁性角度传感器芯片
磁性编码芯片 -在线编程角度位置IC 描述: MT6521是麦歌恩微电子推出的新一代基于水平霍尔及聚磁片(IMC)技术原理的磁性角度和位置检测传感器芯片。该芯片内部包含了两对互成90放置的水平霍尔阵列及聚磁片,能够根据不同的型号配置来实现对XY࿰…...
【数据结构】堆,优先级队列
目录 堆堆的性质大根堆的模拟实现接口实现构造方法建堆入堆判满删除判空获取堆顶元素 Java中的PriorityQueue实现的接口构造方法常用方法PriorityQueue注意事项 练习 堆 如果有一个集合K {k0,k1, k2,…,kn-1},把它的…...
2024 暑假友谊赛 2
Tree Queries - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路:LCA好题,也有看见用时间戳写的,不是很明白. 用LCA非常好写。 要想到,给出k个节点,要确定一条路径,使得给出的k个点要么在路径上,要么和路径中某点的…...
c++ 线程
在 C 中,std::thread 构造函数可以用于将参数传递给线程。这里是一个基本的示例,展示了如何使用 std::thread 来传递参数: #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插件:创建虚拟环境1. 命令方式创建2. 图文方式创建 在虚拟环境中安装Django创建Django项目安装Django插件选择虚拟环境 创建项目工作路径 输入 code . 下载python插件: 创建虚拟环境 1. 命令方式创建 切换在工作目…...
(一)延时任务篇——延时任务的几种实现方式概述
前言 延时任务是我们在项目开发中经常遇到的场景,例如订单超时30分钟自动取消,邮件5分钟后通知等等,对于这样的应用场景,我们又该如何应对呢,尤其是在微服务环境下,如何保证我们的延迟任务并发问题以及数据…...
每天五分钟计算机视觉:目标检测模型从RCNN到Fast R-CNN的进化
本文重点 前面的课程中,我们学习了RCNN算法,但是RCNN算法有些慢,然后又有了基于RCNN的Fast-RCNN,Fast R-CNN是一种深度学习模型,主要用于目标检测任务,尤其在图像中物体的识别和定位方面表现出色。它是R-CNN系列算法的一个重要改进版本,旨在解决R-CNN中计算量大、速度慢…...
环境变量配置文件中两种路径添加方式
环境变量配置文件中两种路径添加方式 文章目录 环境变量配置文件中两种路径添加方式代码示例区别和作用 代码示例 export HBASE_HOME/opt/software/hbase-2.3.5 export PATH$PATH:$HBASE_HOME/binexport SPARK_HOME/opt/software/spark-3.1.2 export PATH$SPARK_HOME/bin:$PAT…...
开放系统互连安全体系结构学习笔记总结
开篇 本文是《网络安全 技术与实践》一书中序章中“开放系统互连安全体系结构”这一块的笔记总结。 定义 开放系统互连(Open System Interconnection, OSI)安全体系结构定义了必需的安全服务、安全机制和技术管理,以及它们在系统上的合理部署…...
linux搭建redis cluster集群
集群介绍: Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。 Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继…...
瀚高数据库初级考试认证
pg_dumpall可以转储全局角色和表空间信息 单选题2分 A. 是 B. 否 回答正确(2分) 答案: A 解析:pg_dumpall备份一个给定集簇中的每一个数据库,并且也保留了集簇范围的数据,如角色和表空间定义。 2. 自定义文件格式必须与pg_restore…...
【java基础】spring中使用到的设计模式
Spring框架在其设计和实现中使用了多种设计模式,这些模式帮助Spring框架保持灵活性、可扩展性和易于集成的特点。以下是一些在Spring框架中常见和重要的设计模式: 工厂模式(Factory Pattern) Spring的核心容器使用了工厂模式&…...
浅层深度学习的概述
在人工智能和机器学习的领域中,“深度学习”已成为一个热门话题。该术语通常与多层神经网络和复杂模型联系在一起,然而,“浅层深度学习”是指那些较为简单而且通常只有一两个隐藏层的神经网络。这种模型在许多任务中表现出色,同时…...
如何找到最快解析速度的DNS
如何找到最快解析速度的DNS DNS,即域名系统(Domain Name System),是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使用户更方便地访问互联网,而不用记住能够被机器直接读取的IP数串。 在浏览网页时,我们通常使用域名,而不是IP地址。当域名在…...
【YashanDB知识库】数据库使用shutdown immediate无响应导致coredump
【标题】数据库使用shutdown immediate无响应导致coredump 【问题分类】数据库维护 【关键词】YashanDB, shutdown immediate, coredump 【问题描述】执行shutdown immediate后,数据库一直没有退出,在操作系统层面强制停止数据库进程时发生coredump。…...
web前端 React 框架面试200题(一)
面试题 1. 简述什么是React ( 概念 )? 参考回答: 1、React是Facebook开发的一款JS库。 2、React一般被用来作为MVC中的V层,它不依赖其他任何的库,因此开发中,可以与任何其他的库集成使用&…...
【前端】JavaScript入门及实战91-95
文章目录 91 DOM92 事件93 文档的加载94 DOM查询(1)95 图片切换的练习 91 DOM <!DOCTYPE html> <html> <head> <title></title> <meta charset"utf-8"><style> </style> </head> <body><button id&…...
vue3在元素上绑定自定义事件弹出虚拟键盘
最近开发中遇到一个需求: 焊接机器人的屏幕上集成web前端网页, 但是没有接入键盘。这就需要web端开发一个虚拟键盘,在网上找个很多虚拟键盘没有特别适合,索性自己写个简单的 图片: 代码: (代码可能比较垃圾冗余,也没时间优化,凑合看吧) 第一步:创建键盘组件 为了方便使用…...
VMware 上安装 CentOS 7 教程 (包含网络设置)
**建议先看一些我安装VMware的教程,有些网络配置需要做一下 1.打开VMware,创建虚拟机 2.勾选自定义,点击下一步 3.点击下一步 4.勾选“稍后安装操作系统”,点击下一步 5.勾选linux,勾选centos7,点击下一步…...
算法 day4 【双指针、快慢指针、环形链表】链表下
⚡刷题计划day4继续,可以点个免费的赞哦~ 下一期将会开启哈希表刷题专题,往期可看专栏,关注不迷路, 您的支持是我的最大动力🌹~ 目录 ⚡刷题计划day4继续,可以点个免费的赞哦~ 下一期将会开启哈希表刷题…...
智能音箱的工作原理
智能音箱的工作原理主要涉及到硬件和软件两个层面的协同工作,以及多个关键技术环节的配合。以下是对智能音箱工作原理的详细解析: 一、硬件层面 智能音箱的硬件组成通常包括主控芯片、麦克风阵列、扬声器、Wi-Fi模块和电源等部分。 主控芯片࿱…...
国际金融入门:国际收支与平衡表解析
在全球化的经济体系中,国际金融已成为我们日常生活不可或缺的一部分。了解国际金融的基础知识,可以帮助我们更好地理解世界经济的动态和趋势。今天,我们将深入探讨国际收支及其平衡表,以及它们是如何影响国家经济。 国际收支&…...
Modbus转BACnet/IP网关的技术实现与应用
引言 随着智能建筑和工业自动化的快速发展,不同通信协议之间的数据交换也变得日益重要。Modbus和BACnet/IP是两种广泛应用于自动化领域的通信协议,Modbus以其简单性和灵活性被广泛用于工业自动化,而BACnet/IP则在楼宇自动化系统中占据主导地…...
数据库连接断开后,DBAPI的数据源如何自动重连
现象 在使用DBAPI的过程中,如果网络抖动导致数据库连接不上,发现DBAPI的数据源不能重连,必须重启DBAPI才能连上数据库 解决办法 在数据源的连接池参数配置druid.breakAfterAcquireFailurefalse注意在企业版的4.1.1及以上版本才可以配置连接…...
Microsoft 365 Office BusinessPro LTSC 2024 for Mac( 微软Office办公套件)
Microsoft 365 Office BusinessPro LTSC 2024是一款专为商业用户设计的办公软件套件,它集成了Word、Excel、PowerPoint等核心应用,并特别包含了Microsoft Teams这一强大的协作工具。Teams将聊天、会议、文件共享、任务管理等功能整合到一个平台上&#x…...
svelte - 1. 基础知识
svelte中文官网 vue和svelt语法对比 掘金-svelte入门简介 文章目录 1、基本页面框架2、动态属性3、嵌套组件4、@html: 插入html标签,显示真实dom元素5、点击事件 on:click={handleClick}6、响应式声明7、父子组件通信8、if-else(1)if(2)if - else(3)if - else if - else…...
挖掘基于边缘无线协同感知的低功耗物联网 (LPIOT) 的巨大潜力
关键词:边缘无线协同感知、低功耗物联网(LPIOT)、无线混合组网、用电监测、用电计量、多角色、计量插座、无线场景感知、多角色运用、后台边缘层,网络边缘层,场景能效管理,场景能耗计算 在数字化和智能化日益加速的今天ÿ…...
iOS开发设计模式篇第一篇MVC设计模式
目录 1. 引言 2.概念 1.Model 1.职责 2.实现 3.和Controller通信 1.Contrller直接访问Model 2.通过委托(Delegate)模式 3.通知 4.KVO 4.设计的建议 2.View 1.职责 2.实现 3.和Controller通信 1. 目标-动作(Target-Action)模式 2…...
【React】全面解析:从基础知识到高级应用,掌握现代Web开发利器
文章目录 一、React 的基础知识1. 什么是 React?2. React 的基本概念3. 基本示例 二、React 的进阶概念1. 状态(State)和属性(Props)2. 生命周期方法(Lifecycle Methods)3. 钩子(Hoo…...
神经网络之卷积神经网络
目录 一、卷积神经网络概述:1.卷积层:1.1卷积核与神经元:1.2卷积层作用:1.3多输出通道概念: 2.池化层:2.1池化层作用: 3.隐藏层与卷积层、池化层关系: 一、卷积神经网络概述…...
【Vue实战教程】之Vue工程化项目详解
Vue工程化项目 随着多年的发展,前端越来越模块化、组件化、工程化,这是前端发展的大趋势。webpack是目前用于构建前端工程化项目的主流工具之一,也正变得越来越重要。本章节我们来详细讲解一下如何使用webpack搭建Vue工程化项目。 1 使用we…...
DBeaver Ultimate 22.1.0 连接数据库(MySQL+Mongo+Clickhouse)
前言 继续书接上文 Docker Compose V2 安装常用数据库MySQLMongo,部署安装好之后我本来是找了一个web端的在线连接数据库的工具,但是使用过程中并不丝滑,最终还是选择了使用 DBeaver ,然后发现 mongo 还需要许可,又折…...
探索PyMuPDF:Python中的强大PDF处理库
探索PyMuPDF:Python中的强大PDF处理库 背景:为何选择PyMuPDF 在数字化时代,PDF文件因其跨平台的兼容性和对格式的严格保持而成为文档交换的通用格式。然而,处理PDF文件往往需要专门的工具或库。这就是PyMuPDF库的用武之地。PyMuP…...
天润融通AI技术助力汽车行业销售革新,邀约到店率翻倍增长
2024年汽车行业增速放缓,市场竞争加剧。在这种背景下,人工智能的加速渗透,各大汽车厂商纷纷引入大模型技术,对传统营销方式进行升级改造,寻找新的增长空间。 一直以来,汽车厂商投入大量预算,对…...
ubuntu安装mysql8.0
文章目录 ubuntu版本安装修改密码取消root跳过密码验证 ubuntu版本 22.04 安装 更新软件包列表 sudo apt update安装 MySQL 8.0 服务器 sudo apt install mysql-server在安装过程中,系统可能会提示您设置 root 用户的密码,请务必牢记您设置的密码。…...