多线程

多线程-基础

进程

什么是进程

- 进程是指在系统中正在运行的一个应用程序
- 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内

比如同时打开QQ、Xcode,系统就会分别启动2个进程

线程

什么是线程

- 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)
- 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行

比如使用酷狗播放音乐、使用迅雷下载电影,都需要在线程中执行

线程的串行

  • 1个线程中任务的执行是串行的
  • 如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务
  • 也就是说,在同一时间内,1个线程只能执行1个任务

比如在1个线程中下载3个文件(分别是文件A、文件B、文件C) 线程串行

多线程

什么是多线程

- 1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务
- 进程 -> 车间,线程 -> 车间工人
- 多线程技术可以提高程序的执行效率

比如同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C)

多线程

多线程的原理

多线程的原理

- 同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
- 多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
- 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象

思考:如果线程非常非常多,会发生什么情况?

CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源

每条线程被调度执行的频次会降低(线程的执行效率降低)

多线程的优缺点

多线程的优点

- 能适当提高程序的执行效率
- 能适当提高资源利用率(CPU、内存利用率)

多线程的缺点

- 开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如     - 果开启大量的线程,会占用大量的内存空间,降低程序的性能
- 线程越多,CPU在调度线程上的开销就越大
- 程序设计更加复杂:比如线程之间的通信、多线程的数据共享

多线程在iOS开发中的应用

什么是主线程

一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”

主线程的主要作用

- 显示\刷新UI界面
- 处理UI事件(比如点击事件、滚动事件、拖拽事件等)

主线程的使用注意

- 别将比较耗时的操作放到主线程中
- 耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验

iOS中多线程的实现方案

iOS中多线程的实现方案

创建和启动线程

一个NSThread对象就代表一条线程

  • 创建、启动线程

    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [thread start];
    // 线程一启动,就会在线程thread中执行self的run方法
    
  • 主线程相关用法

    +(NSThread *)mainThread; // 获得主线程
    -(BOOL)isMainThread; // 是否为主线程
    +(BOOL)isMainThread; // 是否为主线程
    

其他用法

  • 获得当前线程

    NSThread *current = [NSThread currentThread];
    
  • 线程的名字

    -(void)setName:(NSString *)n;
    -(NSString *)name;
    

其他创建线程方式

  • 创建线程后自动启动线程

    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
    
  • 隐式创建并启动线程

    [self performSelectorInBackground:@selector(run) withObject:nil];
    

上述2种创建线程方式的优缺点

优点:简单快捷
缺点:无法对线程进行更详细的设置

线程的状态

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];

线程的状态

控制线程状态

启动线程

- (void)start; 
// 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态

阻塞(暂停)线程

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 进入阻塞状态

强制停止线程

+ (void)exit;
// 进入死亡状态

注意:一旦线程停止(死亡)了,就不能再次开启任务

多线程的安全隐患

资源共享

1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
比如多个线程访问同一个对象、同一个变量、同一个文件

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

安全隐患示例01 – 存钱取钱 安全隐患示例01 – 存钱取钱

安全隐患示例02 – 卖票 安全隐患示例02 – 卖票

安全隐患分析 安全隐患分析

安全隐患解决 – 互斥锁 安全隐患解决 – 互斥锁

安全隐患解决 – 互斥锁

互斥锁使用格式

@synchronized(锁对象) { // 需要锁定的代码  }
注意:锁定1份代码只用1把锁,用多把锁是无效的

互斥锁的优缺点

优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源

互斥锁的使用前提

多条线程抢夺同一块资源

相关专业术语

线程同步
线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
互斥锁,就是使用了线程同步技术

原子和非原子属性

OC在定义属性时有nonatomic和atomic两种选择

atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁

原子和非原子属性的选择

  • nonatomic和atomic对比

    • atomic:线程安全,需要消耗大量的资源
    • nonatomic:非线程安全,适合内存小的移动设备
  • iOS开发的建议

    • 所有属性都声明为nonatomic
    • 尽量避免多线程抢夺同一块资源
    • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

线程间通信

什么叫做线程间通信

在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信

线程间通信的体现

1个线程传递数据给另1个线程
在1个线程中执行完特定任务后,转到另1个线程继续执行任务

线程间通信常用方法

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

多线程代码实现

pthread

pthread在代码中很少使用,我也不会

NSThread

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 创建主线程
    NSThread * thread =[[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"RookieJX"];

    // 开启主线程
    [thread start];
}

- (void)run:(NSString *)param {
    NSLog(@"%@  == %@",[NSThread currentThread],param);
}
@end

点击之后打印结果为

<NSThread: 0x7d19e170>{number = 3, name = (null)}  == RookieJX
number = 3:说明不是在主线程
而且name = (null):如果是在主线程的话name = main
可以设置name:thread.name = @"JX-Thread"; 取出来[NSThread currenntThread].name

多线程安全问题

#import "ViewController.h"

@interface ViewController ()
/** 售票员01 */
@property (nonatomic,strong) NSThread * thread01;
/** 售票员02 */
@property (nonatomic,strong) NSThread * thread02;
/** 售票员03 */
@property (nonatomic,strong) NSThread * thread03;
/** 剩余票数 */
@property (nonatomic,assign) NSInteger tickitCount;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tickitCount = 10;

    self.thread01 = [[NSThread alloc] initWithTarget:self selector:@selector(runTickit) object:nil];
    self.thread01.name = @"售票员01";

    self.thread02 = [[NSThread alloc] initWithTarget:self selector:@selector(runTickit) object:nil];
    self.thread02.name = @"售票员02";

    self.thread03 = [[NSThread alloc] initWithTarget:self selector:@selector(runTickit) object:nil];
    self.thread03.name = @"售票员03";
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.thread01 start];
    [self.thread02 start];
    [self.thread03 start];
}

- (void)runTickit {
    // 这个方法只会执行一次
    while (1) {

        // 先取出剩余票数
        NSInteger count = self.tickitCount;
        if (count > 0) {
            // 让线程暂停一段时间
            [NSThread sleepForTimeInterval:0.1];
            self.tickitCount = count - 1;
            NSLog(@"【%@】 卖了 【%zd】 张票",[NSThread currentThread].name,self.tickitCount);
        } else {
            NSLog(@"【%@】",[NSThread currentThread].name);
            break;
        }
    }
}
@end
打印结果:
2016-05-25 17:15:22.570 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【9】 张票
2016-05-25 17:15:22.570 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【9】 张票
2016-05-25 17:15:22.570 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【9】 张票
2016-05-25 17:15:22.671 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【8】 张票
2016-05-25 17:15:22.671 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【8】 张票
2016-05-25 17:15:22.671 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【8】 张票
2016-05-25 17:15:22.774 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【7】 张票
2016-05-25 17:15:22.774 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【7】 张票
2016-05-25 17:15:22.774 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【7】 张票
2016-05-25 17:15:22.879 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【6】 张票
2016-05-25 17:15:22.879 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【6】 张票
2016-05-25 17:15:22.879 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【6】 张票
2016-05-25 17:15:22.983 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【5】 张票
2016-05-25 17:15:22.983 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【5】 张票
2016-05-25 17:15:22.983 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【5】 张票
2016-05-25 17:15:23.084 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【4】 张票
2016-05-25 17:15:23.084 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【4】 张票
2016-05-25 17:15:23.084 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【4】 张票
2016-05-25 17:15:23.189 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【3】 张票
2016-05-25 17:15:23.189 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【3】 张票
2016-05-25 17:15:23.189 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【3】 张票
2016-05-25 17:15:23.290 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【2】 张票
2016-05-25 17:15:23.290 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【2】 张票
2016-05-25 17:15:23.290 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【2】 张票
2016-05-25 17:15:23.391 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【1】 张票
2016-05-25 17:15:23.391 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【1】 张票
2016-05-25 17:15:23.391 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【1】 张票
2016-05-25 17:15:23.496 JXNSThread[3289:195743] 【售票员03】卖了一张票,还剩下 【0】 张票
2016-05-25 17:15:23.496 JXNSThread[3289:195741] 【售票员01】卖了一张票,还剩下 【0】 张票
2016-05-25 17:15:23.496 JXNSThread[3289:195742] 【售票员02】卖了一张票,还剩下 【0】 张票
2016-05-25 17:15:23.497 JXNSThread[3289:195743] 【售票员032016-05-25 17:15:23.497 JXNSThread[3289:195741] 【售票员012016-05-25 17:15:23.497 JXNSThread[3289:195742] 【售票员02】


解释:卖的票是不安全的,数据是乱的,不安全

解决办法:这时候需要加一个锁

关键字:@synchronized(任意锁)【苹果不推荐使用】
参数为锁对象,可以为任意对象,但是这个对象需要时全局的,因为会有三个线程,这个方法会调用三次,如果是在这个方法内部实例化一个变量 NSObjc * objc = [[NSObjc alloc] init];这时候当不同的线程进来的时候这个锁是不同的,所以依然是没有任何作用
使用:当我们需要有不同的线程,不同的线程需要操作同一个数据的时候就不要使用锁
    #import "ViewController.h"

@interface ViewController ()
/** 售票员01 */
@property (nonatomic,strong) NSThread * thread01;
/** 售票员02 */
@property (nonatomic,strong) NSThread * thread02;
/** 售票员03 */
@property (nonatomic,strong) NSThread * thread03;
/** 剩余票数 */
@property (nonatomic,assign) NSInteger tickitCount;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tickitCount = 10;

    self.thread01 = [[NSThread alloc] initWithTarget:self selector:@selector(runTickit) object:nil];
    self.thread01.name = @"售票员01";

    self.thread02 = [[NSThread alloc] initWithTarget:self selector:@selector(runTickit) object:nil];
    self.thread02.name = @"售票员02";

    self.thread03 = [[NSThread alloc] initWithTarget:self selector:@selector(runTickit) object:nil];
    self.thread03.name = @"售票员03";
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.thread01 start];
    [self.thread02 start];
    [self.thread03 start];
}

- (void)runTickit {

    // 这个方法只会执行一次
    while (1) {


        // 参数为锁对象,可以为任意对象,但是这个对象需要时全局的,因为会有三个线程,这个方法会调用三次,如果是在这个方法内部实例化一个变量 NSObjc * objc = [[NSObjc alloc] init];这时候当不同的线程进来的时候这个锁是不同的,所以依然是没有任何作用
        @synchronized(self) {

            // 先取出剩余票数
            NSInteger count = self.tickitCount;
            if (count > 0) {
                // 让线程暂停一段时间
                [NSThread sleepForTimeInterval:0.1];
                self.tickitCount = count - 1;
                NSLog(@"【%@】卖了一张票,还剩下 【%zd】 张票",[NSThread currentThread].name,self.tickitCount);
            } else {
                NSLog(@"【%@】",[NSThread currentThread].name);
                break;
            }

        }

    }
}
@end

多线程-GCD

简介

什么是GCD

全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
纯C语言,提供了非常多强大的函数

GCD的优势

GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核、四核)
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

任务和队列

GCD中有2个核心概念

任务:执行什么操作
队列:用来存放任务

GCD的使用就2个步骤

定制任务
确定想做的事情

将任务添加到队列中

GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循队列的FIFO原则:先进先出,后进后出

执行任务

GCD中有2个用来执行任务的函数

  • 用同步的方式执行任务

    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    queue:队列
    block:任务
    
  • 用异步的方式执行任务

    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    

同步和异步的区别

同步:只能在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力

队列的类型

GCD的队列可以分为2大类型

  • 并发队列(Concurrent Dispatch Queue)

    • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    • 并发功能只有在异步(dispatch_async)函数下才有效
  • 串行队列(Serial Dispatch Queue)

    • 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

容易混淆的术语

有4个术语比较容易混淆:同步、异步、并发、串行

  • 同步和异步主要影响:能不能开启新的线程

    • 同步:在当前线程中执行任务,不具备开启新线程的能力
    • 异步:在新的线程中执行任务,具备开启新线程的能力
  • 并发和串行主要影响:任务的执行方式

    • 并发:多个任务并发(同时)执行
    • 串行:一个任务执行完毕后,再执行下一个任务

并发队列

GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建

使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority, // 队列的优先级
unsigned long flags); // 此参数暂时无用,用0即可
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列

全局并发队列的优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台

串行队列

GCD中获得串行有2种途径

使用dispatch_queue_create函数创建串行队列
dispatch_queue_t
dispatch_queue_create(const char *label, // 队列名称 
dispatch_queue_attr_t attr); // 队列属性,一般用NULL即可
dispatch_queue_t queue = dispatch_queue_create("cn.itcast.queue", NULL); // 创建
dispatch_release(queue); // 非ARC需要释放手动创建的队列

使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();

各种队列的执行效果

各种队列的执行效果

注意

使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列

线程间通信示例

从子线程回到主线程

dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行耗时的异步操作...
      dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程,执行UI刷新操作
        });
});

延时执行

iOS常见的延时执行有2种方式

  • 调用NSObject的方法

    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    // 2秒后再调用self的run方法
    
  • 使用GCD函数

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      // 2秒后执行这里的代码...
    
    });
    
    解释:可以自己设置队列,是否是全局 改动dispatch_get_main_queue(),
    

一次性代码

使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

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

队列组

有这么1种需求

- 首先:分别异步执行2个耗时的操作
- 其次:等2个异步操作都执行完毕后,再回到主线程执行操作
如果想要快速高效地实现上述需求,可以考虑用队列组
dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的异步操作都执行完毕后,回到主线程...
});

单例模式

单例模式的作用

可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问
从而方便地控制了实例个数,并节约系统资源

单例模式的使用场合

在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)

单例模式在ARC\MRC环境下的写法有所不同,需要编写2套不同的代码

可以用宏判断是否为ARC环境
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif

单例模式 - ARC

ARC中,单例模式的实现

  • 在.m中保留一个全局的static的实例

    static id _instance;
    
  • 重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)

    +(id)allocWithZone:(struct _NSZone *)zone
    {
      @synchronized(self) {
          if (!_instance) {
              _instance = [super allocWithZone:zone];
          }
      }
      return _instance;
    }
    

提供1个类方法让外界访问唯一的实例

    +(instancetype)sharedSoundTool
    {
    @synchronized(self) {
        if (!_instance) {
            _instance = [[self alloc] init];
        }
    }
    return _instance;
    }
  • 实现copyWithZone:方法
    -(id)copyWithZone:(struct _NSZone *)zone
    {
      return _instance;
    }
    

单例模式 – 非ARC

非ARC中(MRC),单例模式的实现(比ARC多了几个步骤) 实现内存管理方法

-(id)retain { return self; }
-(NSUInteger)retainCount { return 1; }
-(oneway void)release {}
-(id)autorelease { return self; }

代码实践

异步 + 并发 : 可以开启多个线程

/**
 *  异步 + 并发 : 可以开启多个线程
 */
- (void)asyncConcurrent {
    // 1.创建一个并发队列
    /**
     *  创建一个并发队列
     *
     *  @param label:相当于队列名字,一般尽量使用唯一标识
     *  @param attr:DISPATCH_QUEUE_SERIAL为队列的  DISPATCH_QUEUE_CONCURRENT为并发的
     *
     *  @return 返回一个队列
     */
//    dispatch_queue_t queue = dispatch_queue_create("JX.test.queue", DISPATCH_QUEUE_CONCURRENT);
    /**
     *  苹果已经创建好了一个全局并发
     *
     *  @param DISPATCH_QUEUE_PRIORITY_DEFAULT 并发优先级,一般设置为默认
     *  @param 0                               苹果推荐0
     *
     *  @return 返回一个全局队列
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        NSLog(@"【当前队列1】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列2】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列3】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列4】%@",[NSThread currentThread]);
    });
}

打印:
2016-05-27 10:31:26.065 JXGCD[1202:44938] 【当前队列3】<NSThread: 0x7b6a9130>{number = 5, name = (null)}
2016-05-27 10:31:26.065 JXGCD[1202:44939] 【当前队列4】<NSThread: 0x7c07a370>{number = 6, name = (null)}
2016-05-27 10:31:26.065 JXGCD[1202:44937] 【当前队列2】<NSThread: 0x7c33e4a0>{number = 4, name = (null)}
2016-05-27 10:31:26.065 JXGCD[1202:44934] 【当前队列1】<NSThread: 0x7c15a870>{number = 3, name = (null)}

解释:可以时间基本是同事的,在不同线程上,并且不是在主线程上:所以是可以创建开启新的线程

同步 + 并发

/**
 *  同步 + 并发 : 不会开启多个线程
 */
- (void)syncConcurrent {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列1】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列2】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列3】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列4】%@",[NSThread currentThread]);
    });

}

打印:
2016-05-27 10:35:03.135 JXGCD[1230:46597] 【当前队列1】<NSThread: 0x7ba31da0>{number = 1, name = main}
2016-05-27 10:35:03.136 JXGCD[1230:46597] 【当前队列2】<NSThread: 0x7ba31da0>{number = 1, name = main}
2016-05-27 10:35:03.136 JXGCD[1230:46597] 【当前队列3】<NSThread: 0x7ba31da0>{number = 1, name = main}
2016-05-27 10:35:03.136 JXGCD[1230:46597] 【当前队列4】<NSThread: 0x7ba31da0>{number = 1, name = main}

解释:时间有先后顺序,都是在主队列中,所以是不会开启多个线程

异步 + 串行

/**
 *  异步 + 串行  : 创建开启一个新的线程
 */
- (void)asyncserial {
    dispatch_queue_t queue = dispatch_queue_create("JX.test.queue", DISPATCH_QUEUE_SERIAL);
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        NSLog(@"【当前队列1】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列2】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列3】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列4】%@",[NSThread currentThread]);
    });
}

打印:
2016-05-27 14:54:35.379 JXGCD[1670:102727] 【当前队列1】<NSThread: 0x7bb3d290>{number = 3, name = (null)}
2016-05-27 14:54:35.379 JXGCD[1670:102727] 【当前队列2】<NSThread: 0x7bb3d290>{number = 3, name = (null)}
2016-05-27 14:54:35.379 JXGCD[1670:102727] 【当前队列3】<NSThread: 0x7bb3d290>{number = 3, name = (null)}
2016-05-27 14:54:35.380 JXGCD[1670:102727] 【当前队列4】<NSThread: 0x7bb3d290>{number = 3, name = (null)}

解释:异步串行,,只会创建开启一个新的线程

同步 + 串行

/**
 *  同步 + 串行 : 不会创建新线程
 */
- (void)syncserial {
    dispatch_queue_t queue = dispatch_queue_create("JX.test.queue", DISPATCH_QUEUE_SERIAL);
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列1】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列2】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列3】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列4】%@",[NSThread currentThread]);
    });
}

打印:
2016-05-27 15:08:36.465 JXGCD[1738:107992] 【当前队列1】<NSThread: 0x78e1b100>{number = 1, name = main}
2016-05-27 15:08:36.465 JXGCD[1738:107992] 【当前队列2】<NSThread: 0x78e1b100>{number = 1, name = main}
2016-05-27 15:08:36.465 JXGCD[1738:107992] 【当前队列3】<NSThread: 0x78e1b100>{number = 1, name = main}
2016-05-27 15:08:36.466 JXGCD[1738:107992] 【当前队列4】<NSThread: 0x78e1b100>{number = 1, name = main}

解释:不会创建新线程

异步函数 + 主队列

- (void)asynicMain {
    // 获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"【当前队列1】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列2】%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"【当前队列3】%@",[NSThread currentThread]);
    });
}

打印:
2016-05-27 23:56:32.944 JXGCD[1128:79365] 【当前队列1】<NSThread: 0x7ff353d07bf0>{number = 1, name = main}
2016-05-27 23:56:32.945 JXGCD[1128:79365] 【当前队列2】<NSThread: 0x7ff353d07bf0>{number = 1, name = main}
2016-05-27 23:56:32.945 JXGCD[1128:79365] 【当前队列3】<NSThread: 0x7ff353d07bf0>{number = 1, name = main}

解释:只会在主线程执行
所以异步函数是可能开新线程,(遇上主队列就不会开启新的线程,在当前线程执行任务)

同步函数 + 主线程

- (void)synicMain {
    // 获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列1】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列2】%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"【当前队列3】%@",[NSThread currentThread]);
    });
}

这样会死,大写的作死。。。。。但是如果这个代码是在其他线程中就不会死。放在主线程中就会死。

barrier:栅栏线程

队列不能为全局的队列

- (void)asynicMain {
    for (NSInteger i=0; i<10; i++) {
        // 获取主队列
        dispatch_queue_t queue = dispatch_queue_create("JX.test", NULL);
        dispatch_async(queue, ^{
            NSLog(@"【当前队列1】%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"【当前队列2】%@",[NSThread currentThread]);
        });
        dispatch_barrier_async(queue, ^{
            NSLog(@"【dispatch_barrier_async】");
        });
        dispatch_async(queue, ^{
            NSLog(@"【当前队列3】%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"【当前队列4】%@",[NSThread currentThread]);
        });
    }
}

打印:
2016-05-28 09:21:27.317 JXGCD[909:32217] 【当前队列1】<NSThread: 0x7f90ba40ca50>{number = 5, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32058] 【当前队列1】<NSThread: 0x7f90ba708c60>{number = 4, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32221] 【当前队列1】<NSThread: 0x7f90ba707210>{number = 6, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32223] 【当前队列1】<NSThread: 0x7f90ba410d70>{number = 11, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32219] 【当前队列1】<NSThread: 0x7f90ba614ad0>{number = 7, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32220] 【当前队列1】<NSThread: 0x7f90ba40cc00>{number = 9, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32218] 【当前队列1】<NSThread: 0x7f90ba7a9b30>{number = 8, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32049] 【当前队列1】<NSThread: 0x7f90ba7a5ac0>{number = 3, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32222] 【当前队列1】<NSThread: 0x7f90ba7aa700>{number = 10, name = (null)}
2016-05-28 09:21:27.317 JXGCD[909:32113] 【当前队列1】<NSThread: 0x7f90ba6014f0>{number = 2, name = (null)}
2016-05-28 09:21:27.318 JXGCD[909:32217] 【当前队列2】<NSThread: 0x7f90ba40ca50>{number = 5, name = (null)}
2016-05-28 09:21:27.318 JXGCD[909:32058] 【当前队列2】<NSThread: 0x7f90ba708c60>{number = 4, name = (null)}
2016-05-28 09:21:27.318 JXGCD[909:32221] 【当前队列2】<NSThread: 0x7f90ba707210>{number = 6, name = (null)}
2016-05-28 09:21:27.318 JXGCD[909:32223] 【当前队列2】<NSThread: 0x7f90ba410d70>{number = 11, name = (null)}
2016-05-28 09:21:27.319 JXGCD[909:32219] 【当前队列2】<NSThread: 0x7f90ba614ad0>{number = 7, name = (null)}
2016-05-28 09:21:27.319 JXGCD[909:32220] 【当前队列2】<NSThread: 0x7f90ba40cc00>{number = 9, name = (null)}
2016-05-28 09:21:27.319 JXGCD[909:32218] 【当前队列2】<NSThread: 0x7f90ba7a9b30>{number = 8, name = (null)}
2016-05-28 09:21:27.319 JXGCD[909:32222] 【当前队列2】<NSThread: 0x7f90ba7aa700>{number = 10, name = (null)}
2016-05-28 09:21:27.319 JXGCD[909:32049] 【当前队列2】<NSThread: 0x7f90ba7a5ac0>{number = 3, name = (null)}
2016-05-28 09:21:27.319 JXGCD[909:32113] 【当前队列2】<NSThread: 0x7f90ba6014f0>{number = 2, name = (null)}
2016-05-28 09:21:27.345 JXGCD[909:32217] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32058] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32221] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32223] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32219] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32220] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32218] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32222] 【dispatch_barrier_async】
2016-05-28 09:21:27.345 JXGCD[909:32049] 【dispatch_barrier_async】
2016-05-28 09:21:27.346 JXGCD[909:32113] 【dispatch_barrier_async】
2016-05-28 09:21:27.346 JXGCD[909:32217] 【当前队列3】<NSThread: 0x7f90ba40ca50>{number = 5, name = (null)}
2016-05-28 09:21:27.346 JXGCD[909:32058] 【当前队列3】<NSThread: 0x7f90ba708c60>{number = 4, name = (null)}
2016-05-28 09:21:27.347 JXGCD[909:32221] 【当前队列3】<NSThread: 0x7f90ba707210>{number = 6, name = (null)}
2016-05-28 09:21:27.347 JXGCD[909:32223] 【当前队列3】<NSThread: 0x7f90ba410d70>{number = 11, name = (null)}
2016-05-28 09:21:27.348 JXGCD[909:32219] 【当前队列3】<NSThread: 0x7f90ba614ad0>{number = 7, name = (null)}
2016-05-28 09:21:27.348 JXGCD[909:32220] 【当前队列3】<NSThread: 0x7f90ba40cc00>{number = 9, name = (null)}
2016-05-28 09:21:27.349 JXGCD[909:32218] 【当前队列3】<NSThread: 0x7f90ba7a9b30>{number = 8, name = (null)}
2016-05-28 09:21:27.349 JXGCD[909:32222] 【当前队列3】<NSThread: 0x7f90ba7aa700>{number = 10, name = (null)}
2016-05-28 09:21:27.349 JXGCD[909:32049] 【当前队列3】<NSThread: 0x7f90ba7a5ac0>{number = 3, name = (null)}
2016-05-28 09:21:27.350 JXGCD[909:32113] 【当前队列3】<NSThread: 0x7f90ba6014f0>{number = 2, name = (null)}
2016-05-28 09:21:27.350 JXGCD[909:32217] 【当前队列4】<NSThread: 0x7f90ba40ca50>{number = 5, name = (null)}
2016-05-28 09:21:27.350 JXGCD[909:32058] 【当前队列4】<NSThread: 0x7f90ba708c60>{number = 4, name = (null)}
2016-05-28 09:21:27.351 JXGCD[909:32221] 【当前队列4】<NSThread: 0x7f90ba707210>{number = 6, name = (null)}
2016-05-28 09:21:27.351 JXGCD[909:32223] 【当前队列4】<NSThread: 0x7f90ba410d70>{number = 11, name = (null)}
2016-05-28 09:21:27.351 JXGCD[909:32219] 【当前队列4】<NSThread: 0x7f90ba614ad0>{number = 7, name = (null)}
2016-05-28 09:21:27.352 JXGCD[909:32220] 【当前队列4】<NSThread: 0x7f90ba40cc00>{number = 9, name = (null)}
2016-05-28 09:21:27.352 JXGCD[909:32218] 【当前队列4】<NSThread: 0x7f90ba7a9b30>{number = 8, name = (null)}
2016-05-28 09:21:27.353 JXGCD[909:32222] 【当前队列4】<NSThread: 0x7f90ba7aa700>{number = 10, name = (null)}
2016-05-28 09:21:27.353 JXGCD[909:32049] 【当前队列4】<NSThread: 0x7f90ba7a5ac0>{number = 3, name = (null)}
2016-05-28 09:21:27.353 JXGCD[909:32113] 【当前队列4】<NSThread: 0x7f90ba6014f0>{number = 2, name = (null)}


解释:栅栏线程会将之前的线程执行完之后再执行之后的程序,但是队列不能为全局的队列

遍历可以同时执行的数组

/**
 *  快速复制:如果是可以同时执行的一个遍历数组,就可以使用GCD来执行,比如说复制
 */
- (void)quiklyCopy {
    /**
     *  快速遍历:同时执行
     *
     *  @param iterations#> 遍历的总数(例如是数组,就是数组count)
     *  @param queue#>      队列
     *  @param size_t       执行操作
     */

    // 源文件路劲
    NSString * from = @"/Users/wangjiaxiang/Desktop/From";
    // 目标文件路劲
    NSString * to = @"/Users/wangjiaxiang/Desktop/To";
    NSFileManager * file = [NSFileManager defaultManager];
    // 源文件路劲下的所有文件
    NSArray * array = [file subpathsAtPath:from];
    // 获取全局队列(并行)
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_apply(array.count, queue, ^(size_t index) {

        NSString * path = array[index];
        // 源文件路径
        NSString * fromPath = [from stringByAppendingPathComponent:path];
        // 目标文件路径
        NSString * toPath = [to stringByAppendingPathComponent:path];

        [file copyItemAtPath:fromPath toPath:toPath error:nil];

        NSLog(@"【当前线程】%@",[NSThread currentThread]);
    });

}

部分打印结果:
2016-05-28 10:29:29.955 JXGCD[1034:65565] 【当前线程】<NSThread: 0x7fa8ba405a40>{number = 1, name = main}
2016-05-28 10:29:29.957 JXGCD[1034:65565] 【当前线程】<NSThread: 0x7fa8ba405a40>{number = 1, name = main}
2016-05-28 10:29:29.958 JXGCD[1034:65607] 【当前线程】<NSThread: 0x7fa8ba7051e0>{number = 2, name = (null)}
2016-05-28 10:29:29.958 JXGCD[1034:65599] 【当前线程】<NSThread: 0x7fa8ba50d170>{number = 3, name = (null)}
2016-05-28 10:29:29.958 JXGCD[1034:65606] 【当前线程】<NSThread: 0x7fa8ba710360>{number = 4, name = (null)}
2016-05-28 10:29:29.959 JXGCD[1034:65607] 【当前线程】<NSThread: 0x7fa8ba7051e0>{number = 2, name = (null)}
2016-05-28 10:29:29.960 JXGCD[1034:65565] 【当前线程】<NSThread: 0x7fa8ba405a40>{number = 1, name = main}

队列组

// 队列组
- (void)group {
    NSString * string1 = @"http://f.hiphotos.baidu.com/image/pic/item/242dd42a2834349bbe78c852cdea15ce37d3beef.jpg";
    NSString * string2 = @"http://b.hiphotos.baidu.com/image/pic/item/91529822720e0cf37e0fe82c0e46f21fbf09aa69.jpg";

    // 创建队列线程
    dispatch_group_t group = dispatch_group_create();
    // 创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 下载图片1
    dispatch_group_async(group, queue, ^{
        NSURL * url = [NSURL URLWithString:string1];
        NSData * data = [NSData dataWithContentsOfURL:url];
        self.image1 = [UIImage imageWithData:data];
    });

    // 下载图片2
    dispatch_group_async(group, queue, ^{
        NSURL * url = [NSURL URLWithString:string2];
        NSData * data = [NSData dataWithContentsOfURL:url];
        self.image2 = [UIImage imageWithData:data];
    });

    // 当组队列执行完成之后会执行这个方法
    dispatch_group_notify(group, queue, ^{
        // 开启位图 (大小为将来显示尺寸的大小)
        UIGraphicsBeginImageContext(CGSizeMake(300, 350));

        // 绘制图片
        [self.image1 drawInRect:CGRectMake(0, 0, 150, 350)];
        [self.image2 drawInRect:CGRectMake(150, 0, 150, 350)];

        //获取图片
        UIImage * image = UIGraphicsGetImageFromCurrentImageContext();

        // 关闭上下文
        UIGraphicsEndImageContext();

        // 在主线程上显示图片
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
}

也可使用非block方法调用

void work(void * str) {

}

- (void)test {
    // 创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    /**
     *  不使用Block的方式
     *
     *  @param group     组
     *  @param queue     队列
     *  @param context#> 参数,一般为后面的函数的参数,一般为NULL
     *  @param work#>    函数
     */
    dispatch_async_f(queue, NULL, work);
}

results matching ""

    No results matching ""