【iOS】——GCD总结

同步和异步的区别

同步执行等待操作完成,而异步执行允许程序在操作完成前继续运行,提高了效率和响应性。这里的关键就是上一个操作需不需要等待当前操作的执行,如果需要就是同步,如果不需要就是异步。

异步有开启新线程的能力但不一定开启,同步没有开启新线程的能力。

两者的区别在于是否等待队列中的任务执行结束,是否具备开启新线程的能力

什么情况下使用串行队列,什么情况下使用并发队列

  • 串行队列(Serial Dispatch Queue):等待现在执行中的处理,也就是每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
  • 并发队列(Concurrent Dispatch Queue):不等待现在执行中的处理,也就是可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)

并发功能只有在异步(dispatch_async)函数下才有效

串行队列用于确保任务按顺序执行,适用于任务间有依赖关系或需要独占访问共享资源的场景,而并发队列则允许多个任务同时执行,适合于能够并行处理的任务以提高性能和效率,如独立的计算或数据处理任务

GCD和NSOperation队列的区别

  • GCD中的队列有串行队列和并发队列,都是采用FIFO原则,也就是创建的新任务总是在队尾,执行的当前任务总是从队头开始。添加到队列中的任务不能取消或暂停。

  • NSOperationQueue有主队列和自定义队列。自定义队列有串行和并发两种,通过设置操作队列的最大并发操作数来实现。添加到队列中的操作可以取消或者暂停。

主队列是什么

主队列其实就是串行队列,只是在默认情况下当前代码放在主队列中,然后主队列中的代码,又都会放到主线程中去执行。

主队列的任务一定在主线程中进行,主线程中进行的任务不一定是主队列的任务还可能是其它队列的任务。

GCD的优先级

GCD中有四个优先级:分别是高优先级(High Priority)、默认优先级(Default Priority)、低优先级(Low Priority)和后台优先级(Background Priority)

如何理解GCD中的同步和异步操作

GCD中的同步操作:是指将block块中的任务添加到队列的队尾时需要等待队头的任务在线程上执行完并且移除后才能添加,这里边有一个等待队头任务完成的过程

GCD中的异步操作:是指将block块中的任务添加到队列的队尾时直接将所有任务全部依次添加到队尾,不用管队头的任务有没有在线程上执行完并移除,这里边就涉及到了不需要等待的意思。

如何理解GCD中的串行队列和并发队列

GCD中的串行队列和并发队列都遵循FIFO(先进先出)原则,也就是说先添加到队列中的任务先出去,后添加到队列中的任务后出去。

串行队列:

串行队列保证队列中的任务会按照它们被添加到队列中的顺序依次执行(先添加的任务会先开始执行,后添加的任务必须等待前面的任务完成才能开始)。

如何保证的呢?

当一个任务被添加到串行队列中时,无论是同步还是异步添加,GCD 都会将这个任务放入队列的末尾无非是要不要等待的过程。串行队列的调度器会从队列的头部取出任务放到线程上来执行,无论开启多少的新线程,它限制每次只执行一个任务,一旦当前任务完成,才会从队列中取出下一个任务开始执行

并发队列

并发队列有让队列中的任务在多个线程上同时进行的能力,这取决于是否开启新线程。

  • 如果当前只开启了一个线程那么队列中的任务还是需要等待队头的任务在线程上执行完才能执行后面的任务,也就跟串行队列是一样的。

  • 如果当前开启了多个线程比如说三个线程,那么队列会分配三个任务到这三个线程上同时执行,哪个线程先执行完就将队头的任务添加到哪个线程上。

如何理解任务的执行顺序

任务的执行顺序是任务被添加到队列中的顺序。如果是顺序执行的话就是任务按照队列的FIFO原则,先进的先执行,后进的后执行。如果是乱序的话就不是先进的先执行,后进的后执行。造成乱序的原因就是多线程中的并发执行。

队列+任务 组合方式

1.串行队列+同步执行

同步执行是是指将block块中的任务添加到队列的队尾时需要等待队头的任务在线程上执行完并且移除后才能添加。串行队列限制每次只执行一个任务,一旦当前任务完成,才会从队列中取出下一个任务开始执行。因此任务是顺序执行的。

2.串行队列+异步执行

异步执行是将所有任务依次添加到队列中,不会等待任务完成。并发队列有让队列中的任务在多个线程上同时进行的能力,并且在异步执行会开启新线程。所以任务是乱序执行的

3.并发队列+同步执行

同步执行时将block块中的任务添加到队列的队尾时需要等待队头的任务在线程上执行完并且移除后才能添加。并发队列有让队列中的任务在多个线程上同时进行的能力。所以这里关键要看是否开启新线程。因为是同步执行所以没有开启新线程,所以按顺序执行。

如果在同步执行之前,在该并发队列中异步执行了添加任务的操作的话,就会开启新线程。此时再同步执行的话,虽然添加任务这个操作需要等待线程上的任务执行完才能进行,但此时已经有多个线程,这样就不能保证任务的顺序。此时同步和异步执行的任务都是乱序的。

如果是同步执行之后再进行异步操作,虽然开启了新线程,但是同步执行的任务在开启新线程之前就已经全部执行完了,这个新线程只负责处理异步操作中的任务,也就是同步执行的还是顺序,异步执行的是乱序。

因此下面两段代码的运行结果是有区别的,第一段代码是不按照顺序执行,第二段代码按照顺序执行

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrentQueue, ^{NSLog(@"Task 1 started");sleep(0);NSLog(@"Task 1 completed");});dispatch_sync(concurrentQueue, ^{NSLog(@"Task 2 started");sleep(2);NSLog(@"Task 2 completed");});dispatch_sync(concurrentQueue, ^{NSLog(@"Task 3 started");sleep(2);NSLog(@"Task 3 completed");});

在这里插入图片描述

dispatch_sync(concurrentQueue, ^{NSLog(@"Task 1 started");sleep(0);NSLog(@"Task 1 completed");});dispatch_sync(concurrentQueue, ^{NSLog(@"Task 2 started");sleep(2);NSLog(@"Task 2 completed");});dispatch_async(concurrentQueue, ^{NSLog(@"Task 3 started");sleep(2);NSLog(@"Task 3 completed");});

在这里插入图片描述

4.并发队列+异步执行

异步执行是将所有任务依次添加到队列中,不会等待任务完成。并发队列有让队列中的任务在多个线程上同时进行的能力,并且在异步执行会开启新线程。所以任务是乱序执行的

5.主队列+同步执行

首先需要知道的是主队列的任务一定是在主线程上进行的。同步执行需要等待线程上的任务执行完之后才能将任务添加到队尾。将任务同步添加到队尾这个操作其实也是个任务,而这个任务一般是在主线程上进行。此时就形成了主队列中的任务需要等待将任务添加到队列的这个操作的任务执行完才能进行,而将任务添加到队列这个操作需要等待主线程上的任务执行完才能进行,形成了主队列和主线程相互等待的局面,造成了死锁。

如果是在 其他线程 调用 主队列+同步执行,则不会阻塞 主队列,也就不会造成死锁

6.主队列+异步执行

主队列的任务在主线程上进行。异步执行是不需要等待线程上的任务执行完就直接添加。当异步添加到队列的任务在主线程上执行完后就会执行队列里的任务。因此也不会造成死锁。因为主队列的本质是个串行队列,所以还是按顺序执行。

GCD的API

dispatch_set_target_queue(设置目标队列)

第一个参数为要变更优先级的队列,第二个参数为指定的队列所属的优先级(也就是第一个队列变换的优先级)

dispatch_set_target_queue(changedQueue, targetQueue);

还可以作为Dispatch Queue的执行阶层:如果在多个串行队列中用dispatch_set_target_queue函数指定目标为某一个串行队列,那么原本要并发执行的多个串行队列现在只能在指定的串行队列上处理,也就变成非并发处理。

dispatch_after(设置延迟执行)

实现在指定时间(例如 1 秒)之后执行某个任务,需要注意的是dispatch_after 方法并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 2.0 秒后异步追加任务代码到主队列,并开始执行NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程});
Dispatch Group(队列组)

当我们想分别并发执行多个耗时任务,然后这几个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组

先创建队列和队列组,将多个任务放到队列中再把队列放到队列组中,最后调用 dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait
回到当前线程继续向下执行(会阻塞当前线程)。

- (void)useDispatchGroup {//创建队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//创建队列组dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{NSLog(@"blk0");});dispatch_group_async(group, queue, ^{NSLog(@"blk1");});dispatch_group_async(group, queue, ^{NSLog(@"blk2");});dispatch_group_notify(group, queue, ^{NSLog(@"Done!");});
}
dispatch_group_notify

监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。

也就是当所有任务都执行完成之后,才执行 dispatch_group_notify 相关 block 中的任务。
第一个参数为队列组,第二个参数是要追加执行的任务

 dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"add task");});
dispatch_group_wait

暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_group_enter、dispatch_group_leave

dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1
dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。

当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务。

dispatch_barrier_async(栅栏方法)

主要用来将并发队列中的多个任务分割成两组来执行,只有当第一组的任务执行完并且执行了dispatch_barrier_async中追加的任务后才能执行第二组的任务。

这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的 如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果

dispatch_once(限制一次方法)

使用 dispatch_once 方法能保证某段代码在程序运行过程中只被执行 1 次,并且即使在多线程的环境下,dispatch_once 也可以保证线程安全。

/*** 一次性代码(只执行一次)dispatch_once*/
- (void)once {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{// 只执行 1 次的代码(这里面默认是线程安全的)});
}

dispatch_apply(快速迭代方法)

按照指定的次数将指定的任务追加到指定的队列中,快速迭代一组任务,并等待全部队列执行结束。主要用于需要快速并行处理大量相同类型任务的场景,如图像处理、数据处理或任何形式的批处理任务。

在串行队列中无法体现出它的作用,因为队列中的任务始终是一次执行一个。

在并发队列中如果开启了多个线程的话就能在多个线程中同时展开执行,效率特别高。

void dispatch_apply(size_t iterations,dispatch_queue_t queue,dispatch_apply_iterate_t iterator
);
  • iterations 是你要执行的任务数量。
  • queue 是你希望在其中执行任务的队列,通常是并发队列。
  • iterator 是一个指向函数的指针,这个函数将为每个迭代执行一次。

用下面这段代码举个例子:

for (int i = 0; i < 100000; i++) {NSString *queueN = [NSString stringWithFormat:@"c%dqueue", i];dispatch_queue_t cQueue = dispatch_queue_create(queueN.UTF8String, DISPATCH_QUEUE_CONCURRENT);dispatch_async(cQueue, ^{NSLog(@"the queue = %s", dispatch_queue_get_label(cQueue));});
}

在上面代码中模拟了我们想要在多线程循环执行任务场景,但是突然创建很多线程或造成线程爆炸,可以使用dispatch_apply来实现,因为dispatch_apply的线程全部由GCD管理。从而避免了手动创建线程爆炸问题。

// 解决线程爆炸方案
dispatch_queue_t queue = dispatch_queue_create("xxQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_apply(100000, queue, ^(size_t t) {NSLog(@"the current thread = %@, t = %ld", NSThread.currentThread, t);
});

dispatch_suspend & dispatch_resume(暂停和恢复)

dispatch_suspend函数可以随时暂停某个队列,等需要执行的时候使用dispatch_resume恢复即可。

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_suspend(queue);dispatch_resume(queue);

dispatch_semaphore(信号量)

  • emaphore叫做”信号量”
  • 信号量的初始值,可以用来控制线程并发访问的最大数量
  • 信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
//表示最多访问5个线程
dispatch_semaphore_create(5);
// 如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码
// 如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成>0,就让信号量的值减1,然后继续往下执行代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
// 让信号量的值+1
dispatch_semaphore_signal(self.semaphore);

主要控制的是能够访问共享资源或执行临界区的线程数量,而不是直接控制线程开启的数量。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // 创建一个初始值为1的信号量dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrentQueue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量,获得资源访问权限NSLog(@"Task 1: Accessing shared resource");// 执行对共享资源的访问dispatch_semaphore_signal(semaphore); // 释放资源访问权限
});dispatch_async(concurrentQueue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量,获得资源访问权限NSLog(@"Task 2: Accessing shared resource");// 执行对共享资源的访问dispatch_semaphore_signal(semaphore); // 释放资源访问权限
});// 为了确保主线程等待所有任务完成,可以使用 dispatch_sync 或 dispatch_group
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{dispatch_group_leave(group);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

什么场景下会出现死锁?

有三种场景会出现死锁:

  • 主队列+同步执行
  • 异步串行队列嵌套同步串行队列
  • 信号量阻塞主线程
场景一:同步+主队列[self task1];
dispatch_sync(dispatch_get_main_queue(), ^{[self task2];NSLog(@"同步线程执行主队列任务");
});
[self task3];(1)执行task1
(2)阻塞同步线程,把task2加入到主队列的队尾
(3)task3需要等待task2执行完成后执行,但是此时task2又排在task3后面,所以造成了死锁场景二:异步串行队列嵌套同步串行队列dispatch_queue_t myQueue = dispatch_queue_create("com.bg.sQueue", DISPATCH_QUEUE_SERIAL);
[self task1];
dispatch_async(myQueue, ^{[self task2];dispatch_sync(myQueue, ^{[self task3];});[self task4];
});
[self task5];(1)执行task1
(2)执行task5
(3)执行task2
(4)阻塞同步线程,把task3加入到队列myQueue的队尾
(5)task4需要等待task3执行完成后执行,但是此时task3又排在task4后面,所以造成了死锁场景三:信号量阻塞主线程dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_main_queue(), ^{dispatch_semaphore_signal(sem);NSLog(@"the sem +1");
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"the sem -1");主线程中dispatch_semaphore_wait一直等着dispatch_semaphore_signal改变信号量(+1操作),但是dispatch_semaphore_wait却阻塞了主线程导致dispatch_semaphore_signal无法执行,从而造成了死锁。

GCD的任务能取消吗?

GCD本身没有提供取消任务的API,但是可以通过操作来间接设置取消任务比如:

  • 在 block 内部添加一个布尔变量作为取消标志,然后在外部改变这个标志的值。block 在执行时可以检查这个标志是否被设置为取消状态,如果被设置,则提前结束 block 的执行。

  • DispatchSource 可以用来监控文件描述符、定时器等事件,但也可以用于模拟任务取消。可以创建一个 DispatchSourceTimer,并在定时器触发时检查是否应该取消任务。

如何用GCD实现任务间的依赖?

实现任务间的依赖通常涉及到确保一个任务在另一个任务完成之后才开始执行。

  • 因此可以使用串行队列来解决。因为串行队列保证任务按顺序执行。当你向串行队列中添加任务时,GCD 会确保前一个任务完成之后才开始执行下一个任务

  • 在并发队列中,还可以使用dispatch_barrier_async 来确保在执行屏障任务之前,所有之前提交到队列的任务都已经完成。这可以用来实现任务间的依赖。

如何使用GCD来实现控制最大并发数?

可以使用**dispatch_semaphore**

首先,创建一个信号量,其初始值等于你想要的最大并发数。每当开始一个新任务时,尝试减少信号量的值。如果信号量的值为0,新的任务将被阻塞,直到有任务完成并释放信号量。

如何实现一个常驻线程?(线程保活)

  • 使用无限循环

在一个线程中使用无限循环(如 while 循环)来持续执行任务或监听事件。为了防止线程占用过多的 CPU 资源,可以在循环中加入适当的延时。

  • 使用 dispatch_source_t

dispatch_source_t 是 GCD 中一种可以用于监听事件的对象,比如定时器、文件描述符等。可以创建一个 dispatch_source_t 定时器,让它定期触发并执行任务。

  • 使用NSThread

使用NSThread创建线程并在其中执行无限循环或监听事件。

怎样利用GCD实现多读单写呢(怎么实现一个多读单写的模型)?

通过栅栏异步调用的方式,分配写操作到并发队列当中。

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

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

相关文章

合作伙伴中心Partner Center中添加了Copilot预览版

目录 一、引言 二、Copilot 功能概述 2.1 Copilot 简介 2.2 Copilot 的核心功能 2.3 Copilot 的访问和使用 三、Copilot 的使用方法 3.1 Copilot 功能区域 3.2 Copilot 使用示例 3.2.1 编写有效提示 3.2.2 使用反馈循环 四、负责任的人工智能 4.1 Copilot 结果的可…

Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签

本教程主要实现【Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签】。 本文源码&#xff1a;https://gitee.com/songfayuan/go-zero-demo 教程源码分支&#xff1a;master 分支&#xff08;_examples/word-template/fill-word-template.go&…

win 10 局域网共享

1&#xff0c;打开共享 控制面板\网络和 Internet\网络和共享中心\高级共享设置 &#xff08;在控制面板界面建议使用大图片或小图标容易找到目标&#xff09; 或者直接复制红色部分&#xff0c;然后打开此电脑&#xff0c;粘贴到地址栏直接回车即可直接到达几面 打开如下2个…

达梦数据库一体机在宜昌市财政局上线了!

财政作为国家治理的基础和重要支柱&#xff0c;其数字化转型已成为构建现代财政制度的必由之路&#xff0c;引领着财政管理体系向更高效、更智能的方向迈进。 达梦数据全面助力财政信息化转型与智能化发展&#xff0c;采用 DAMEGN PAI I 系列数据库一体机&#xff0c;为宜昌市财…

Unity Camera

课程目标 1. 了解摄像机&#xff08;camera&#xff09;不同视角的设计与实现&#xff1b;2. 感受在不同摄像机视角下观察虚拟场景。 喜欢玩游戏或者看3D动漫的朋友可以回忆在虚拟场景中摄像头的运动变化带来的视觉感受&#xff0c;例如&#xff1a;摄像头给场景中的主角来个…

马来西亚原生静态IP注册的账号稳定吗?

马来西亚作为东南亚重要的经济体之一&#xff0c;其网络基础设施和互联网服务水平在近年来有了显著提升。静态IP作为一种固定的互联网协议地址&#xff0c;对于某些特定的网络应用和需求非常重要。本文将围绕马来西亚原生静态IP注册的账号稳定性进行探讨&#xff0c;分析其在不…

【Python系列】Python 字典合并

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

pytorch基础模块:Tensorboard、Dataset、Transforms、Dataloader

Tensorboard、Dataset、Transforms、Dataloader 该文档主要参考【土堆】的视频教程&#xff1a;pytorch入门教程–土堆 一、Tensorboard 安装tensorboard&#xff1a;pip install tensorboard 使用步骤&#xff1a; 引入相关库&#xff1a;from torch.utils.tensorboard i…

LinkedList接口源码解读

LinkedList 接口源码解读&#xff08;一&#xff09; 前言 因为追求质量&#xff0c;所以写的较慢。大概在接下来的三天内会把LinkedList源码解析出完。大概还有两篇文章。废话不多说&#xff0c;正片开始&#xff01; 大家都知道&#xff0c;LinkedList是在Java底层中是由 …

手机上音乐如何转换成MP3格式?分享5款音频格式转换APP

手机上音乐如何转换成MP3格式&#xff1f;相信很多外出办公或者不经常使用电脑的工作人士&#xff0c;学生党&#xff0c;媒体从业者都有这样的疑惑和需求。不同设备和应用可能支持不同的音频格式&#xff0c;导致某些情况下需要将音乐文件转换为MP3格式以确保兼容性。下面&…

操作系统|day4.Linux、Linux内核、Linux负载、Linux文件存储

文章目录 LinuxLinux内核定义功能态 Linux负载定义 Linux文件存储链接分类区别使用场景 拷贝 Linux Linux内核 定义 内核是操作系统的核心&#xff0c;具有很多最基本功能&#xff0c;它负责管理系统的进程、内存、设备驱动程序、文件和网络系统&#xff0c;决定着系统的性能…

.NET周刊【7月第4期 2024-07-28】

国内文章 .NET 高性能缓冲队列实现 BufferQueue https://mp.weixin.qq.com/s/fUhJpyPqwcmb3whuV3CDyg BufferQueue 是一个用 .NET 编写的高性能的缓冲队列实现&#xff0c;支持多线程并发操作。 项目地址&#xff1a;https://github.com/eventhorizon-cli/BufferQueue 项目…

【虚拟仿真】Unity3D中实现2DUI显示在3D物体旁边

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群:398291828大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 这篇文章来实现2DUI显示在3D物体旁边,当我们需要在3D模型旁边显示2DUI的时候,比如人物的对…

grep工具的使用

grep [options]…… pattern [file]…… 工作方式&#xff1a; grep 在一个或者多个文件中搜索字符串模板&#xff0c;如果模板中包括空格&#xff0c;需要使用引号引起来&#xff0c;模 板后的所有字符串会被看作是文件名。 工作结果&#xff1a;如果模板搜索成功&#xf…

Springboot+Vue在线水果(销售)商城管理系统-附源码与配套论文

1.1 研究背景 互联网概念的产生到如今的蓬勃发展&#xff0c;用了短短的几十年时间就风靡全球&#xff0c;使得全球各个行业都进行了互联网的改造升级&#xff0c;标志着互联网浪潮的来临。在这个新的时代&#xff0c;各行各业都充分考虑互联网是否能与本行业进行结合&#xf…

Java:进程和线程

文章目录 进程线程的概念和区别总结如何创建线程1.继承Thread重写run2.实现Runnable重写run3.继承Thread重写run,通过匿名内部类来实现4. 实现Runnable重写run,通过匿名内部类来实现5.基于lambda表达式来创建 虚拟线程 并发编程: 通过写特殊的代码&#xff0c;把多个CPU核心都利…

Shell编程 --基础语法(1)

文章目录 Shell编程基础语法变量定义变量使用变量命令的使用只读变量删除变量 传递参数字符串获取字符串长度字符串截取 数组定义方式关联数组获取数组的长度 总结 Shell编程 Shell是一种程序设计语言。作为命令语言&#xff0c;它交互式解释和执行用户输入的命令或者自动地解…

【人工智能基础三】卷积神经网络基础(CNN)

文章目录 1. 卷积神经网络结构2. 卷积神经网络计算2.1. 卷积层计算2.2. 池化层计算2.3. 全连接层计算 3. 典型卷积神经网络3.1. AlexNet3.2. VGGnet 卷积神经网络(Convolutional Neural Network&#xff0c;CNN)是一类包含卷积计算且具有深度结构的前馈神经网络(Feedforward Ne…

计算机毕业设计Python+Tensorflow股票推荐系统 股票预测系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI

1、用pycharm打开项目&#xff0c;一定要打开包含manage.py文件所在文件夹 2、配置解释器&#xff1a;建议使用Anaconda(Python 3.8(base))&#xff0c;低于3.8版本的&#xff0c;页面会不兼容 3、安装依赖库&#xff1a;打开pycharm的终端&#xff0c;输入&#xff1a; pip in…

Docker-学习笔记(借助宝塔面板)

ubuntu环境 一、安装 可以参考官网进行或其他博客进行安装 1.进入宝塔面板 进图Docker菜单&#xff0c;查看是否提示安装。 2.查看是否安装 查看版本 docker -v 证明已经安装 二、常用命令 1.查看版本 docker -v 2.启动、停止、重启docker systemctl start docker…