iOS-多线程介绍

时间:2021-05-13 07:40:17

一、前言部分

 最近在面试,重新温习了一遍多线程,希望加深一遍对于多线程的理解。

1、什么是进程?

1).要了解线程我们必须先了解进程,通俗来讲进程就是在系统中运行的一个应用程序。

2).每个线程之间是独立存在的,分别运行在其专用的且受保护的内存空间中。

3).比如打开QQ或Xcode系统会分别开启两个进程 如图:

iOS-多线程介绍

4)、我们可以通过"活动监视器"查看Mac系统中所开启的进程。

2、什么是线程?

1).一个进程要想执行任务必须得有线程,即一个进程至少要有一个线程。

2).线程是进程的基本执行单元,一个进程(程序)的所有任务都是在线程中执行的。

3).比如使用酷狗播放音乐、使用迅雷下载电影都需要在线程中运行 如图:

iOS-多线程介绍

3、什么是线程的串行?

1).一个线程中任务是串行执行的(顺序执行)的,也就是说一个线程同一时间内只能执行一个任务。

2).串行执行图解,比如一个线程下载3个文件(文件A、B、C)

iOS-多线程介绍

4、什么是多线程?

1).一个进程中可以开启多个线程,每个线程可以并发(同时)执行不同的任务。

2).类似关系列举:进程---->车间;线程---->车间工人

3).多线程图解,比如同时开启3个线程分别下载3个文件(文件A、B、C

iOS-多线程介绍

 

5、多线程原理

1).同一时间CPU只能执行一个线程,只有一个线程在工作(执行)。

2).多线程并发(同时)执行,其实是CPU快速的在多个线程之间调度(切换)。

3).如果CPU调度线程的速度够快,就会造成多线程并发执行的假象。

4).多线程的缺点:

   1、每个线程都会占用一定的内存空间(默认情况下:主线程占用1MB,子线程占用512KB),

        如果开启线程过多会占用大量的内存空间因而造成程序性能降低。

   2、线程越多CPU调度线程上的开销就越大(类似工厂工人越多,工厂开销也越大)。

   3、使程序设计更复杂:比如多线程的数据通信,多线程之间的数据共享。

5).多线程的优点:

   1、能适当提高程序的执行效率。

   2、能适当提高资源利用率(CPU、内存的利用率)

6、什么是主线程?

 1).一个iOS程序开启后默认会开启一个线程,这个线程被称为"主线程"或"UI线程"。

 2).主线程的主要作用:

    1、显示/刷新UI界面

    2、处理UI事件(比如点击事件、滚动事件、拖拽事件等)

 3).主线程注意点:

    1、别将耗时的操作放在主线程中,耗时操作放在主线程中会造成程序卡顿的问题。

7、耗时操作Demo演示   

1)、直接在主线程中运行的Demo

iOS-多线程介绍iOS-多线程介绍
 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
2 //获取当前执行方法和当前线程
3 //number==1 主线程
4 //number!=1 其他线程、子线程、次线程
5 NSLog(@"%s----%@",__func__,[NSThread currentThread]);
6
7 //直接在主线程中运行 造成UI操作卡顿
8 [self longTimeOperation];
9 }
10
11 #pragma mark-耗时操作
12 -(void)longTimeOperation{
13 for (int i=0; i<20000; i++) {
14 NSLog(@"%d",i);
15 }
16 }
View Code

2)、在子线程中运行的Demo

iOS-多线程介绍iOS-多线程介绍
 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
2 //获取当前执行方法和当前线程
3 //number==1 主线程
4 //number!=1 其他线程、子线程、次线程
5 NSLog(@"%s----%@",__func__,[NSThread currentThread]);
6
7 //将耗时操作放在子线程中执行,不影响UI的操作
8 [self performSelectorInBackground:@selector(longTimeOperation) withObject:nil];
9 }
10
11 #pragma mark-耗时操作
12 -(void)longTimeOperation{
13 for (int i=0; i<20000; i++) {
14 NSLog(@"%d",i);
15 }
16 }
View Code

8、iOS中多线程的实现方案

技术方案 简介 语言 线程生命周期 使用频率
pthread
  • 一套通用的多线程API
  • 适用于Unix\Linux\Windows等系统
  • 跨平台可移植
  • 使用难度大
C 程序员管理 几乎不用
NSThread
  • 使用更加面向对象
  • 简单易用直接操作线程对象
OC 程序员管理 偶尔使用
GCD
  • 旨在替换NSThread等线程技术
  • 充分利用设备的多核
C 自动管理 经常使用
NSOperation
  • 基于GCD(底层是GCD)
  • 比GCD多了一些更简单实用的功能
  • 使用更加面向对象
OC 自动管理 经常使用

9、pthread 的使用 

iOS-多线程介绍iOS-多线程介绍
 1 #import <pthread.h>
2 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
3 [self test];
4 }
5
6 //使用pthread创建线程
7 -(void)test{
8 //声明一个线程变量
9 pthread_t threadID;
10
11 /*
12 参数:
13 1、要开的线程的变量
14 2、线程的属性
15 3、要在这个子线程中执行的函数(任务)
16 4、这个函数(任务)需要传递的参数
17 */
18 //pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);
19
20 id str=@"hello";
21 //id 需要转成void * 需要使用__bridge进行桥连
22 //1、这里只是临时把str对象转成void *在这里临时使用,不改变这个对象的所有权
23 //2、把对象所有权交出去,在这个函数把str转成void *
24 //如果使用MRC 这里不需要使用桥连可以直接使用这个参数
25 //ARC自动内存管理,本质是编译器特性,是在程序编译的时候,编译器在适合的地方帮我们添加retain、release、autorelease
26 pthread_create(&threadID, NULL, run, (__bridge void*)str);
27 }
28
29 #pragma mark-耗时操作
30 void *run(void *prama){
31 //void * 相当于OC里面的id
32 //__bridge 桥连操作
33 NSString *str=(__bridge NSString*)prama;
34 for (int i=0; i<20000; i++) {
35 NSLog(@"%d----%@----%@",i,[NSThread currentThread],str);
36
37 }
38 return NULL;
39 }
View Code

10、NSThread 的创建方式 

iOS-多线程介绍iOS-多线程介绍
 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
2 //[self test1];
3
4 //[self test2];
5
6 [self test3];
7 }
8
9 #pragma mark-线程的创建方式
10 //创建线程方式1
11 -(void)test1{
12 //实例化一个线程对象
13 NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(run1) object:nil];
14 //线程启动方法
15 [thread start];
16 }
17
18 //创建线程方式2
19 -(void)test2{
20 //创建线程后自动启动线程
21 [NSThread detachNewThreadSelector:@selector(run2:) toTarget:self withObject:@"KK"];
22 }
23
24 //创建线程方式3
25 -(void)test3{
26 //隐式创建线程并启动
27 [self performSelectorInBackground:@selector(run2:) withObject:@"Hi"];
28
29 /*
30 test2、test3 两个线程创建方法的优缺点:
31 优点:简单快捷
32 缺点:无法对线程更详细的设置
33 */
34 }
35
36 #pragma mark-耗时操作
37 -(void) run1{
38 for (int i=0; i<200; i++) {
39 NSLog(@"%d----%@",i,[NSThread currentThread]);
40 }
41 }
42 -(void) run2:(NSString*)param{
43 for (int i=0; i<200; i++) {
44 NSLog(@"%d----%@---%@",i,[NSThread currentThread],param);
45 }
46 }
View Code

 11、线程的属性介绍

iOS-多线程介绍iOS-多线程介绍
 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
2 [self test];
3 }
4
5 #pragma mark-线程的属性
6 -(void)test{
7 //实例化一个线程对象
8 NSThread *threadA=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
9 //线程名称,用于区分多个线程
10 threadA.name=@"Thread A";
11 //线程的优先级 0.0~1.0 默认值 0.5
12 //实际开发中一般不修改优先级的值
13 threadA.threadPriority=0.1;
14 //线程启动方法
15 [threadA start];
16
17 // //实例化一个线程对象
18 // NSThread *threadB=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
19 // //线程名称,用于区分多个线程
20 // threadB.name=@"Thread B";
21 // //线程的优先级 0.0~1.0 默认值 0.5
22 // threadB.threadPriority=1.0;
23 // //线程启动方法
24 // [threadB start];
25 }
26
27
28 #pragma mark-耗时操作
29 -(void) run{
30 for (int i=0; i<200; i++) {
31 NSLog(@"%d----%@",i,[NSThread currentThread]);
32 }
33 }
View Code

12、GCD的使用实例

iOS-多线程介绍iOS-多线程介绍
 1 #pragma mark - GCD演练
2 /**
3 并发队列,同步执行
4 */
5 - (void)gcdDemo4 {
6 // 1. 队列
7 dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
8
9 // 2. 同步执行任务
10 for (int i = 0; i < 10; i++) {
11 dispatch_sync(queue, ^{
12 NSLog(@"%@ %d", [NSThread currentThread], i);
13 });
14 }
15 }
16
17 /**
18 并发队列,异步执行
19 */
20 - (void)gcdDemo3 {
21 // 1. 队列
22 dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
23
24 // 2. 异步执行任务
25 for (int i = 0; i < 10; i++) {
26 dispatch_async(queue, ^{
27 NSLog(@"%@ %d", [NSThread currentThread], i);
28 });
29 }
30
31 }
32
33 /**
34 串行队列,异步执行
35 */
36 - (void)gcdDemo2 {
37 // 1. 队列
38 dispatch_queue_t queue = dispatch_queue_create("itcast", NULL);
39
40 // 2. 异步执行任务
41 for (int i = 0; i < 10; i++) {
42 dispatch_async(queue, ^{
43 NSLog(@"%@ %d", [NSThread currentThread], i);
44 });
45 }
46 }
47
48 /**
49 串行队列,同步执行(开发中非常少用)
50 */
51 - (void)gcdDemo1 {
52
53 // 1. 队列
54 // dispatch_queue_t queue = dispatch_queue_create("icast", DISPATCH_QUEUE_SERIAL);
55 dispatch_queue_t queue = dispatch_queue_create("icast", NULL);
56 NSLog(@"执行前----");
57
58 // 执行任务
59 for (int i = 0; i < 10; i++) {
60 NSLog(@"调度----");
61
62 // 在队列中"同步"执行任务,串行对列添加同步执行任务,会立即被执行
63 dispatch_sync(queue, ^{
64 NSLog(@"%@ %d", [NSThread currentThread], i);
65 });
66 }
67 NSLog(@"for 后面");
68 }

View Code

13、NSOperation的使用实例 

iOS-多线程介绍iOS-多线程介绍
 1 #pragma mark - 基本演练
2 // MARK: - 线程间通讯
3 - (void)opDemo5 {
4 NSOperationQueue *q = [[NSOperationQueue alloc] init];
5 [q addOperationWithBlock:^{
6 NSLog(@"耗时操作 %@", [NSThread currentThread]);
7
8 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
9 NSLog(@"更新UI %@", [NSThread currentThread]);
10 }];
11 }];
12 }
13
14 // MARK: - 更简单的
15 - (void)opDemo4 {
16 NSOperationQueue *q = [[NSOperationQueue alloc] init];
17
18 for (int i = 0; i < 10; i++) {
19 [q addOperationWithBlock:^{
20 NSLog(@"down %@ %@", [NSThread currentThread], @(i));
21 }];
22 }
23 }
24
25 // MARK: - NSBlockOperation
26 - (void)opDemo3 {
27 NSOperationQueue *q = [[NSOperationQueue alloc] init];
28
29 for (int i = 0; i < 10; i++) {
30 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
31 NSLog(@"down %@ %@", [NSThread currentThread], @(i));
32 }];
33
34 [q addOperation:op];
35 }
36 }
37
38 // MARK: - NSInvocationOperation
39 - (void)opDemo2 {
40 NSOperationQueue *q = [[NSOperationQueue alloc] init];
41
42 for (int i = 0; i < 10; i++) {
43 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)];
44
45 [q addOperation:op];
46 }
47 }
48
49 - (void)opDemo1 {
50 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"Invocation"];
51
52 // start 会立即在当前线程执行 selector 方法
53 // [op start];
54
55 // 将操作添加到队列,会自动异步执行
56 NSOperationQueue *q = [[NSOperationQueue alloc] init];
57 [q addOperation:op];
58 }
59
60 - (void)downloadImage:(id)obj {
61 NSLog(@"%@ %@", [NSThread currentThread], obj);
62 }
View Code 

14、线程的状态介绍

iOS-多线程介绍

iOS-多线程介绍iOS-多线程介绍
 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
2 [self test];
3 }
4
5 #pragma mark-线程的属性
6 -(void)test{
7 //实例化一个线程对象
8 NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
9 //放到可调度线程池,等待被调度 这时候是准备就绪状态
10 [thread start];
11 }
12
13
14 #pragma mark-耗时操作
15 -(void) run{
16
17 NSLog(@"%s",__func__);
18 //线程进来就睡眠2秒
19 //[NSThread sleepForTimeInterval:2.0];
20
21 //睡到指定的时间点
22 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
23
24 for (int i=0; i<200; i++) {
25 //满足一个条件后,阻塞线程的执行
26 if (i==10) {
27 [NSThread sleepForTimeInterval:2.0];
28 }
29
30 //一旦达到某个条件,就强制终止线程的执行
31 if (i==100) {
32 //一旦终止就不能重新启动,后面的代码就不会被执行
33 [NSThread exit];
34 }
35 NSLog(@"%d----%@",i,[NSThread currentThread]);
36 }
37 }
View Code

15、多线程的安全隐患

1).一块资源可能会被多个线程共享,也就是多个线程可能访问同一块资源。

2).比如多个线程访问同一个对象、同一个变量、同一个文件。

3).当多个线程访问同一个资源时,很容易引发数据错乱和数据安全问题。

16、解决多线程安全问题

1).添加互斥锁解决多线程访问同一资源造成的数据安全问题。

2).互斥锁使用格式:@synchronized (self) {//需要锁定的代码}

3).互斥锁的优缺点:

  优点:能有效防止因多线程抢夺同一资源造成的数据安全问题。

  缺点:需要消耗大量的CPU资源(因此苹果不推荐使用互斥锁)

4).互斥锁的使用前提:多个线程抢夺同一资源。

17、利用属性的原子性保证数据的安全性

1).atomic 原子属性--默认属性

2).原子属性内部使用的是 自旋锁

3).自旋锁与互斥锁的异同

  共同点:都可以锁定一段代码,同一时间内,只有一个线程能执行这段锁定的代码。

  不同点:互斥锁 在锁定的时候其他线程在等待的时候会进入休眠 等待条件满足时需要重新唤醒。

  自旋锁在锁定的时候,其他线程等待的时候做死循环一直等待条件满足,一旦条件满足就立马执行,

  少了一个唤醒的过程。