iOS中多线程知识总结:进程、线程、GCD、串行队列、并行队列、全局队列、主线程队列、同步任务、异步任务等(有示例代码)

 

原文:iOS中多线程知识总结:进程、线程、GCD、串行队列、并行队列、全局队列、主线程队列、同步任务、异步任务等(有示例代码)

进程

正在运行中的程序被称作进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间。


线程

进程中的一个对立的执行路径被称为线程,一个进程中至少包含一条线程,该线程被称为主线程。

多线程

多创建一条或者多条线程的,目的就是为了开启一条新的执行路径,运行指定的代码,与主线程中的代码实现同时运行。

优势

  • 充分发挥多核处理器优势,将不同线程任务分配给不同的处理器,真正进入“并行运算”状态。
  • 将耗时的任务分配到其他线程执行,由主线程负责统一更新界面会使应用程序更加流畅,用户体验更好。
  • 当硬件处理器的数量增加,程序会运行更快,而程序无需做任何调整。

弊端

  • 新建线程会消耗内存空间和CPU时间,线程太多会降低系统的运行性能。

误区

  • 多线程技术是为了并发执行多项任务,不会提高单个算法本身的执行效率。 

iOS中的三种多线程技术

NSThread

  • 使用NSThread对象建立一个线程非常方便。
  • 但是!要使用NSThread管理多个线程非常困难,不推荐使用。
  • 技巧!使用[NSThread currentThread]跟踪任务所在线程,适用于这三种技术。

NSOperation/NSOperationQueue

  • 是使用GCD实现的一套Objective-C的API。
  • 是面向对象的线程技术。
  • 提供了一些在GCD中不容易实现的特性,如:限制最大并发数量、操作之间的依赖关系。
  • 苹果官方现在建议大家使用该多线程技术,但现在仍然很多人使用GCD技术,因为GCD使用较为方便。

GCD —— Grand Central Dispatch

  • Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法,称作大中心调度。该方法在Mac OS X 10.6雪豹中首次推出,并随后被引入到了iOS4.0中。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。
  • 是基于C语言的底层API。
  • 用Block定义任务,使用起来非常灵活便捷。
  • 提供了更多的控制能力以及操作队列中所不能使用的底层函数。 

队列

队列是先进先出(FIFO)结构的,其主要的任务主要是负责线程的创建、回收工作,不论什么队列和什么任务都不需要程序员参与,减轻了程序员的工作。

GCD的队列

  • 运行在主线程中的主队列。
  • 3 个不同优先级的后台队列。
  • 一个优先级更低的后台队列(用于 I/O)。

自定义队列

  • 串行队列
  • 并行队列。
  • 自定义队列非常强大,建议在开发中使用。在自定义队列中被调度的所有Block最终都将被放入到系统的全局队列中和线程池中。 
    GCD队列示意图
    从图中可以看出串行队列、并行队列都属于默认优先级的GCD队列。 
    提示:不建议使用不同优先级的队列,因为如果设计不当,可能会出现优先级反转,即低优先级的操作阻塞高优先级的操作。

串行队列

队列中的任务只会顺序执行(类似跑步),创建如下:

dispatch_queue_t q = dispatch_queue_create(“threadName”, DISPATCH_QUEUE_SERIAL);
  • 1
  • 1

“threadName”是队列名称,用于在调试时辅助。

并行队列

队列中的任务通常会并发执行(类似赛跑),创建如下:

dispatch_queue_t q = dispatch_queue_create("threadName", DISPATCH_QUEUE_CONCURRENT);
  • 1
  • 1

“threadName”是队列名称,用于在调试时辅助。

全局队列

是系统的,苹果为了方便多线程的设计,提供一个全局队列,供所有的APP共同使用,直接拿过来(GET)用就可以;与并行队列类似,但调试时,无法确认操作所在队列,获得方法如下:

dispatch_queue_t q =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
  • 1
  • 1

主线程队列

每一个应用程序对应唯一一个主队列,直接GET即可;在多线程开发中,使用主队列更新UI,获得方法如下:

dispatch_queue_t q = dispatch_get_main_queue();
  • 1
  • 1

任务

同步任务

必须按照顺序执行的任务,利用如下代码添加一个同步任务:

dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)
  • 1
  • 1

异步任务

执行顺序不确定,那个任务线抢占到CPU资源谁先执行,会并发执行,利用如下代码添加一个异步任务:

dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
  • 1
  • 1

不同队列中添加不同任务运行结果的总结

串行队列+同步任务

不会创建新的线程,所有任务会在同一线程中按顺序执行,运行如下代码:

- (void)gcdDemo1 {
  // 1.创建队列
  dispatch_queue_t q =
      dispatch_queue_create("kwjThread1", DISPATCH_QUEUE_SERIAL);
  // 2.向队列中添加任务
  for (int i = 0; i < 10; i++) {
    dispatch_sync(q, ^{
      NSLog(@"%@ %d", [NSThread currentThread], i);
    });
  }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

运行结果: 
运行结果

串行队列+异步任务

操作需要一个子线程,会新建线程、线程的创建和回收不需要程序员参与,操作顺序执行,是“最安全的选择”,运行如下代码:

- (void)gcdDemo1 {
  // 1.创建队列
  dispatch_queue_t q =
      dispatch_queue_create("kwjThread1", DISPATCH_QUEUE_SERIAL);
  // 2.向队列中添加任务
  for (int i = 0; i < 10; i++) {
    dispatch_async(q, ^{
      NSLog(@"%@ %d", [NSThread currentThread], i);
    });
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果: 
运行结果

并行队列+同步任务

不会创建新的线程,所有任务会在同一线程中按顺序执行,运行如下代码:

- (void)gcdDemo2 {
  // 创建队列
  dispatch_queue_t q =
      dispatch_queue_create("kwjThread2", DISPATCH_QUEUE_CONCURRENT);
  // 同步任务,不会开启新的线程,按顺序进行
  for (int i = 0; i < 10; i++) {
    dispatch_sync(q, ^{
      NSLog(@"%@ %d", [NSThread currentThread], i);
    });
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果: 
这里写图片描述

并行队列+异步任务

操作会新建多个线程(有多少任务,就开N个线程执行)、操作无序执行;队列前如果有其他任务,会等待前面的任务完成之后再执行;场景:既不影响主线程,又不需要顺序执行的操作!运行如下代码:

- (void)gcdDemo2 {
  // 创建队列
  dispatch_queue_t q =
      dispatch_queue_create("kwjThread2", DISPATCH_QUEUE_CONCURRENT);
  // 异步任务,有多少任务,就会开启N个线程
    for (int i = 0; i < 10; i++) {
      dispatch_async(q, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
      });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

运行结果: 
运行结果

全局队列+同步任务

不会创建新的线程,所有任务会在同一线程中按顺序执行,运行如下代码:

- (void)gcdDemo3{
    // 取得全局队列
    // 记住:在开发中永远用DISPATCH_QUEUE_PRIORITY_DEFAULT
    dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 添加任务
    // 同步任务
    NSLog(@"%@KWJ", [NSThread currentThread]);
    for (int i = 0; i < 10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果: 
运行结果

全局队列+异步任务

操作会新建多个线程、操作无序执行,队列前如果有其他任务,会等待前面的任务完成之后再执行,运行如下代码:

 - (void)gcdDemo3{
    // 取得全局队列
    dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 添加任务

    // 异步任务
    for (int i = 0; i < 10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
}
@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果: 
这里写图片描述

全局队列与并发队列比较

  • 不需要创建,直接GET就能用
  • 两个队列的执行效果相同
  • 全局队列没有名称,调试时,无法确认准确队列

主队列+同步任务

主队列中添加同步任务会发生阻塞(死锁),运行如下代码:

- (void)gcdDemo4{
    // 获得主队列
    dispatch_queue_t q = dispatch_get_main_queue();
    NSLog(@"%@,我是:",[NSThread currentThread]);
    // 添加同步任务
    dispatch_sync(q, ^{
        NSLog(@"%@,KWJ",[NSThread currentThread]);
    });
    NSLog(@"haha");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

运行结果: 
这里写图片描述

在主队列开启同步任务,为什么会阻塞线程?

在主队列开启同步任务,因为主队列是串行队列,里面的线程是有顺序的,先执行完一个线程才执行下一个线程,而主队列始终就只有一个主线程,主线程是不会执行完毕的,因为他是无限循环的,除非关闭应用程序。因此在主线程开启一个同步任务,同步任务会想抢占执行的资源,而主线程任务一直在执行某些操作,不肯放手。两个的优先级都很高,最终导致死锁,阻塞线程了。

主队列+异步操作

异步任务在主线程上运行,同时是保持队形的,操作都应该在主线程上顺序执行的,不存在异步的概念,运行如下代码:

- (void)gcdDemo4{
    // 获得主队列
    dispatch_queue_t q = dispatch_get_main_queue();
    NSLog(@"%@KWJ",[NSThread currentThread]);
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ - %d", [NSThread currentThread], i);
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果: 
运行结果

在主队列开启异步任务,不会开启新的线程而是依然在主线程中执行代码块中的代码。为什么不会阻塞线程?

主队列开启异步任务,虽然不会开启新的线程,但是他会把异步任务降低优先级,等闲着的时候,就会在主线程上执行异步任务。


不同队列中嵌套dispatch_sync的结果

嵌套代码

dispatch_sync(q, ^{
    NSLog(@"同步任务 %@", [NSThread currentThread]);
    dispatch_sync(q, ^{
        NSLog(@"同步任务 %@", [NSThread currentThread]);
    });
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在串行队列中

串行队列,嵌套上述代码会死锁,但是会执行嵌套同步操作之前的代码。

在并行队列中

并行队列,嵌套上述代码,不会死锁,都在主线程上执行。

在全局队列中

全局队列,嵌套上述代码,不会死锁,都在主线程上执行。

在主队列中

主队列,嵌套上述代码直接死锁。


GCD小结

  • 通过GCD,开发者不用再直接跟线程打交道,只需要向队列中添加代码块即可。
  • GCD在后端管理着一个线程池,GCD不仅决定着代码块将在哪个线程被执行,它还根据可用的系统资源对这些线程进行管理。从而让开发者从线程管理的工作中解放出来,通过集中的管理线程,缓解大量线程被创建的问题。
  • 使用GCD,开发者可以将工作考虑为一个队列,而不是一堆线程,这种并行的抽象模型更容易掌握和使用。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值