GCD使用(多线程004)

来源:互联网 发布:php分割字符串 编辑:程序博客网 时间:2024/06/06 07:39


1. RunLoop介绍

  • Runloop被称为消息循环或事件循环
  • 每个线程里,都有一个消息循环
  • 默认情况下,主线程开启消息循环,子线程不开启

目的

  • 保证程序不退出
  • 负责处理输入事件
  • 如果没有事件处理,会让程序进行休眠

消息类型(事件类型)

  • Input Sources(输入源)

Input for sources such as mouse and keyboard events from the window system, NSPort objects, and NSConnection objects.

包括键盘鼠标事件,NSPort,NSConnection,等。

  • Timer Sources(定时源)

An NSRunLoop object also processes NSTimer events.

就是定时器NSTimer

常用的循环模式

  • NSDefaultRunLoopMode

The mode to deal with input sources other than NSConnection objects. This is the most commonly used run-loop mode. Available in iOS 2.0 and later.

  • NSRunLoopCommonModes

Objects added to a run loop using this value as the mode are monitored by all run loop modes that have been declared as a member of the set of “common" modes; see the description of CFRunLoopAddCommonMode for details.

如何使用

  1. 创建消息
  2. 把消息加入到消息循环中,并指定循环的模式

加入的2种方法

[self performSelector:@selector(demo2) onThread:thread withObject:nil waitUntilDone:NO];[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

只有当消息的循环模式和当前线程中消息循环的模式像符合,消息才能被运行

子线程中的消息循环

子线程中默认不开启消息循环

开启方式

  1. Run方法

    [[NSRunLoop currentRunLoop] run];

    缺点:开启之后无法关闭,并且在他之后的代码都不会被运行,因为他是一个死循环

  2. runUntilDate

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
  1. 苹果推荐方法
    BOOL shouldKeepRunning = YES;        // global NSRunLoop *theRL = [NSRunLoop currentRunLoop]; while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

问题演示

  • 线程没有执行方法,线程立马销毁
NSThread *thread = [[NSThread alloc] init];[thread start];[self performSelector:@selector(demo2) onThread:thread withObject:nil waitUntilDone:NO];
  • 线程指定方法,但是没有开启消息循环
 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];    [thread start];    [self performSelector:@selector(demo2) onThread:thread withObject:nil waitUntilDone:NO];    ..........................    - (void)demo {    NSLog(@"over");}

2. GCD简介

  • 全称是Grand Central Dispatch
  • 纯C语言,提供了非常多强大的函数

优点

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

核心组成部分

任务&队列

任务:要干什么事情

队列:用来存放任务

如何做

  1. 确定要做的事情
  2. 把任务加到队列里
  3. 没了

GCD会自动将任务从队列里面取出,放到对应的贤臣各种去执行

任务取出的原则是先进先出FIFO(First in first out)

举例

//任务dispatch_block_t block;//队列dispatch_queue_t queue;//把任务放到队列里dispatch_async(queue, block);

3. 队列执行任务的方式

同步执行任务

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);queue:队列block:任务

异步执行任务

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

区别

同步:在当前线程上执行 异步:在其他线程上执行


4. 队列的种类

并发队列(Concurrent Dispatch Queue)

  • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
  • 并发功能只有在异步(dispatch_async)函数下才有效
dispatch_queue_t queue = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);

串行队列(Serial Dispatch Queue)

  • 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
dispatch_queue_t queue = dispatch_queue_create("SERIAL", DISPATCH_QUEUE_SERIAL);

5. 同步、异步、并发、串行的基本概念

同步和异步决定了要不要开启新的线程

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

并发和串行决定了任务的执行方式

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

6. 串行队列的执行

串行队列的同步执行

  • 不开线程,同步执行(在当前线程执行)
#define DISPATCH_QUEUE_SERIAL NULL    //串行队列    //dispatch_queue_t q = dispatch_queue_create("test", NULL);    dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);    for (int i = 0; i < 10; i++) {        //同步执行        dispatch_sync(q, ^{            NSLog(@"%@ -- %d",[NSThread currentThread],i);        });    }

串行队列的异步执行

  • 开一个线程,顺序执行
 //只有一个线程,因为是串行队列,只有一个线程就可以按顺序执行队列中的所有任务    dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);    for (int i = 0; i < 10; i++) {        //异步执行        dispatch_async(q, ^{            NSLog(@"%@ -- %d",[NSThread currentThread],i);        });    }

7. 并行队列的执行

并行队列,异步执行

  • 开多个线程,异步执行,每次开启多少个线程是不固定的(线程数,不由我们控制),线程数是由gcd来决定的
    dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);    for (int i = 0; i < 10; i++) {        //异步执行        dispatch_async(q, ^{            NSLog(@"%@ -- %d",[NSThread currentThread],i);        });    }

并行队列,同步执行

  • 不开线程,顺序执行,与串行队列的同步执行一模一样

8. 系统提供的队列

iOS已经为我们准备好了2个常用队列主队列列和全局并发队列,开发中用的很多

主队列

  • 主队列是负责在主线程调度任务的
  • 会随着程序启动一起创建
  • 主队列只需要获取不用创建
    dispatch_queue_t queue = dispatch_get_main_queue();

主队列,异步任务

  • 不开线程,同步执行
  • 主队列特点:如果主线程正在执行代码暂时不调度任务,等主线程执行结束后在执行任务
  • 主队列又叫 全局串行队列

主队列,同步执行

  • 程序执行不出来(死锁)死锁的原因

当程序执行到下面这段代码的时候

dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"%@ -- %d",[NSThread currentThread],i); });

主队列:如果主线程正在执行代码,就不调度任务

同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完成,再执行下一个任务此时互相等待,程序无法往下执行(死锁)

全局并发队列

  • 为了方便程序员开发提供的队列,与并发队列行为相同

9. 同步异步串行并发的组合结果

 全局并发队列手动创建串行队列主队列同步(sync)没有开启新线程,串行执行任务没有开启新线程,串行执行任务死锁异步(async)有开启新线程,并行执行任务开启新线程,并行执行任务没有开启新线程,串行执行任务

10. GCD延时函数使用

dispatch_after的定义

dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);

dispatch_after的参数

  • 参数1 dispatch_time_t when

多少纳秒之后执行

  • 参数2 dispatch_queue_t queue

任务添加到那个队列

  • 参数3 dispatch_block_t block

要执行的任务

例子

//延时操作    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{    });

11. GCD一次执行和单例实现

有的时候,在程序开发中,有些代码只想从程序启动就只执行一次,典型的应用场景就是“单例”

// MARK: 一次性执行- (void)once {    static dispatch_once_t onceToken;    NSLog(@"%ld", onceToken);    dispatch_once(&onceToken, ^{        [NSThread sleepForTimeInterval:1.0];        NSLog(@"一次性吗?");    });    NSLog(@"come here");}
  • dispatch_once是线程安全的,因为内部也有一把锁,苹果推荐使用该种方式来实现单例模式

测试

单例的2中实现

  • 使用 dispatch_once 实现单例
+ (instancetype)sharedSingleton {    static id instance;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        instance = [[self alloc] init];    });    return instance;}
  • 使用互斥锁实现单例
+ (instancetype)sharedSync {    static id syncInstance;    @synchronized(self) {        if (syncInstance == nil) {            syncInstance = [[self alloc] init];        }    }    return syncInstance;}
  • 对比结果
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    long largeNumber = 1000 * 1000;    // 测试互斥锁    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();    for (long i = 0; i < largeNumber; ++i) {        [Singleton sharedSync];    }    NSLog(@"互斥锁: %f", CFAbsoluteTimeGetCurrent() - start);    // 测试 dispatch_once    start = CFAbsoluteTimeGetCurrent();    for (long i = 0; i < largeNumber; ++i) {        [Singleton sharedSingleton];    }    NSLog(@"dispatch_once: %f", CFAbsoluteTimeGetCurrent() - start);}

12. GCD任务组

使用场景:在组里的异步任务都执行完毕后,再去执行其他操作,例如下载歌曲,等所有的歌曲都下载完毕之后 转到 主线程提示用户

  • 方式一:dispatch_group_async常用用法
/** 调度组-在一组异步代码执行完毕后,统一获得通知 应用场景:将一组图像异步缓存到本地之后统一获得通知! */- (void)group1 {    // 队列    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);    // 调度组    dispatch_group_t group = dispatch_group_create();    // 添加异步    dispatch_group_async(group, queue, ^{        [NSThread sleepForTimeInterval:1.0];        NSLog(@"download A %@", [NSThread currentThread]);    });    dispatch_group_async(group, queue, ^{        [NSThread sleepForTimeInterval:0.5];        NSLog(@"download B %@", [NSThread currentThread]);    });    dispatch_group_async(group, queue, ^{        NSLog(@"download C %@", [NSThread currentThread]);    });    // 调度组通知 - 监听群组中,所有异步执行的代码完成后,得到通知    // 异步监听!    dispatch_group_notify(group, dispatch_get_main_queue(), ^{        NSLog(@"下载完成 %@", [NSThread currentThread]);    });    NSLog(@"come here");}
  • 方式二:添加enter & leave
// MARK: - 调度组 2- (void)group2 {    // 1. 调度组    dispatch_group_t group = dispatch_group_create();    // 2. 队列    dispatch_queue_t q = dispatch_get_global_queue(0, 0);    // dispatch_group_enter & dispatch_group_leave 必须成对出现    dispatch_group_enter(group);    dispatch_group_async(group, q, ^{        NSLog(@"任务 1 %@", [NSThread currentThread]);        // dispatch_group_leave 必须是 block 的最后一句        dispatch_group_leave(group);    });    dispatch_group_enter(group);    dispatch_group_async(group, q, ^{        NSLog(@"任务 2 %@", [NSThread currentThread]);        // dispatch_group_leave 必须是 block 的最后一句        dispatch_group_leave(group);    });    // 4. 阻塞式等待调度组中所有任务执行完毕    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    // 5. 判断异步    NSLog(@"OVER %@", [NSThread currentThread]);}

注意:不要跟dispatch_barrier_async混淆,一个是所有任务都完成后,一个是某个任务中的一段代码需要交给另一个线程有序的串行执行


1 0
原创粉丝点击