【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];});
    }
    

    运行结果:

    在这里插入图片描述

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

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

相关文章

ChatTTS真人文本转语音模型,富有韵律与情感,且免费开源

上期图文教程&#xff0c;我们分享了微软TTS真人转语音大模型&#xff0c;但是微软的TTS模型只有针对新用户免费一年&#xff0c;其他用户都是收费的&#xff0c;虽然微软开源了部分TTS的功能&#xff0c;但是针对真人类似的富有情感的TTS模型并没有进行开源&#xff0c;本期介…

软件测试基础1--功能测试

1、什么是软件测试&#xff1f; 软件是控制计算机硬件运行的工具。 软件测试&#xff1a;使用技术手段验证软件是否满足使用需求&#xff0c;为了发现软件功能和需求不相符合的地方&#xff0c;或者寻找实际输出和预期输出之间的差异。 软件测试的目的&#xff1a;减少软件缺陷…

学习笔记之JAVA篇(0724)

p 方法 方法声明格式&#xff1a; [修饰符1 修饰符2 ...] 返回值类型 方法名&#xff08;形式参数列表&#xff09;{ java语句;......; } 方法调用方式 普通方法对象.方法名&#xff08;实参列表&#xff09;静态方法类名.方法名&#xff08;实参列表&#xff09; 方法的详…

【YashanDB知识库】YashanDB的JDBC/OCI驱动如何设置字符编码

问题现象 Oracle、Mysql数据库链接串&#xff0c;JDBC驱动连接串可以指定客户端的编码格式&#xff1a; jdbc:mysql://hostname:port/database_name?useUnicodetrue&characterEncodingutf8mb4 jdbc:oracle:thin://hostname:port/service_name?NLS_LANGUAGEAMERICAN&am…

【SQL语句大全(MySQL)】

SQL语法 添加删除修改查询基本查询条件查询分组函数/聚合函数分组查询排序分页查询&#xff08;限制查询&#xff09;多表查询连接查询根据年代分类连接查询根据连接方式分类1、内连接2、左外连接3、右外连接 多张表连接的语法格式 嵌套查询 SQL语句书写顺序 添加 INSERT INTO…

构建生成工具cmake的使用(1)

ps:本文是对cmake的基础讲解&#xff0c;掌握后解决70-80%情况是足以应对的&#xff0c;后续会对cmake有进阶内容。 一 前言 CMake 是一个工具&#xff0c;帮助开发者管理和自动化软件项目的构建过程。它使用一个叫做CMakeLists.txt 的文本文件来描述项目的组织结构、编译选项…

oracle数据库下的定时任务,如何创建Jobs

oarcle中写存储过程&#xff0c;上面的文章中已经介绍过了&#xff0c;这次是写好存储过程后&#xff0c;在Jobs文件夹中新建job,达到定时执行任务的目的。 1、在plsql数据库中&#xff0c;找到左边的Jobs,右键点击新建&#xff0c;如下图&#xff1a; 2、按照下图将相应的项添…

【Gitlab】SSH配置和克隆仓库

生成SSH Key ssh-keygen -t rsa -b 4096 私钥文件: id_rsa 公钥文件:id_rsa.pub 复制生成的ssh公钥到此处 克隆仓库 git clone repo-address 需要进行推送和同步来更新本地和服务器的文件 推送更新内容 git push <remote><branch> 拉取更新内容 git pull &…

LINUX高性能服务器框架

1.服务器模型&#xff1a; 1). C/S模型 &#xff1a; 此模型就是一个服务器能给多个客户端提供服务&#xff0c;但所以资源都被服务端所占有&#xff0c;客户端想要获取只能通过请求连接服务端去获取。由于客户端的请求访问是异步的&#xff0c;所以需要一个手段进行此类事件的…

基于微信小程序的课堂考勤系统的设计与实现(论文+源码)_kaic

基于微信小程序的课堂考勤系统的设计与实现 摘 要 在高校教育普及的今天&#xff0c;学生人数日益增多&#xff0c;为保证课堂质量&#xff0c;教师多要在课前进行考勤。因此本设计提出基于微信小程序的课堂考勤系统&#xff0c;增加了定位功能&#xff0c;避免了“假打卡”…

前端开发知识(一)-html

1.前端开发需掌握的内容&#xff1a; 2.前端开发的三剑客&#xff1a;html、css、javascript Vue可以简化JavaScpript流程。 Element&#xff08;饿了么开发的&#xff09; &#xff1a;前端组件库。 Ngix&#xff1a;前端服务器。 3.前端开发工具&#xff1a;vscode 1)按…

基于Java+SpringBoot+Vue的学生心理咨询评估系统

前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 哈喽兄弟们&#xff0c;好久不见哦&#xff5…

乐尚代驾六订单执行一

加载当前订单 需求 无论是司机端&#xff0c;还是乘客端&#xff0c;遇到页面切换&#xff0c;重新登录小程序等&#xff0c;只要回到首页面&#xff0c;查看当前是否有正在执行订单&#xff0c;如果有跳转到当前订单执行页面 之前这个接口已经开发&#xff0c;为了测试&…

Linux嵌入式学习——数据结构——线性表的链式结构

线性表的链式存储 解决顺序存储的缺点&#xff0c;插入和删除&#xff0c;动态存储问题。 特点&#xff1a; 线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素&#xff0c;存储单元可以是连续的&#xff0c;也可以不连续。可以被存储在任意内存未被占…

四、单线程多路IO复用+多线程业务工作池

文章目录 一、前言1 编译方法 二、单线程多路IO复用多线程业务工作池结构三、重写Client_Context类四、编写Server类 一、前言 我们以及讲完单线程多路IO复用 以及任务调度与执行的C线程池&#xff0c;接下来我们就给他结合起来。 由于项目变大&#xff0c;尝试解耦项目&#…

47.简易电压表的设计与验证(2)

&#xff08;1&#xff09;Verilog 代码&#xff1a; module adc_collect(input clk ,input reset_n ,input [7:0] adc_data ,output clk_adc );wire clk_adc_a ;…

提交高通量测序处理数据到 GEO --- 操作流程

❝ 写在前面 由于最近在提交课题数据到 NCBI 数据库&#xff0c;整理了相关笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. 提交高通量测序数据到 GEO --- 说明书 2. 提交高通量测序原…

基于机器学习的二手房价格分析与预测设计与实现

概述 随着西安房地产市场的不断发展和变化&#xff0c;对二手房价格的准确预测变得至关重要。本研究旨在利用机器学习技术对西安市二手房价格进行深入分析与预测&#xff0c;通过对原始数据进行数据预处理和特征提取&#xff0c;以构建有效的预测模型。通过数据分析和可视化&a…

LabVIEW学习-LabVIEW处理带分隔符的字符串从而获取数据

带分隔符的字符串很好处理&#xff0c;只需要使用"分隔符字符串至一维字符串数组"函数或者"一维字符串数组至分隔符字符串"函数就可以很轻松地处理带分隔符地字符串。 这两个函数所在的位置为&#xff1a; 函数选板->字符串->附加字符串函数->分…

第13周 简历职位功能开发与Zookeeper实战

第13周 简历职位功能开发与Zookeeper实战 本章概述1. Mysql8窗口函数over使用1.1 演示表结构与数据1.2 案例1:获取男女总分数1.3 案例2****************************************************************************************本章概述 1. Mysql8窗口函数over使用 参考案例…