iOS 【Multithreading-GCD 同步/异步函数 和 串行/并发队列 的6种搭配使用及介绍】

★★ 同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)

★ 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。
★ 而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

★★ 简单的生活中的例子就是

★ 同步: 你打电话问书店老板有没有 **<<多线程编程>>** 这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。这其中你电话不挂断,(等待返回结果),接收不到返回结果,不继续下面的操作。
★ 异步:书店老板直接告诉你我查一下啊,查好了打电话给你,然后你直接挂电话了(不返回结果),你继续该干哈干哈,等老板然后查好了,他会主动打电话给你,在这里老板通过“回电话”这种方式来回调。


应用到iOS的这两个函数中:

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

同步 方式执行任务:

  1. dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);  

异步 方式执行任务

  1. dispatch_async(dispatch_queue_t queue, dispatch_block_t block); 


这两个函数都需要传递一个队列,和一个block参数。
对于 dispatch_async 来说,它把block放到队列中,之后立刻返回,继续往下执行
对于 dispatch_sync 来说,我把block放到队列后,等待block执行完返回,才继续往下执行

★ 看下实际的代码:

- (void)a
{
    //串行队列(前面的参数是队列名称,后面的参数是创建的是 串行队列 还是 并发队列,如果传NULL表示是串行队列。)
    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);
    
    //异步立刻返回
    dispatch_async(_serialQueue, ^{ NSLog(@"1-----%@",[NSThread currentThread]);});
    
    NSLog(@"2-----%@",[NSThread currentThread]);
    
    dispatch_async(_serialQueue, ^{ NSLog(@"3");});
    
    NSLog(@"4");
}

- (void)b
{
    //串行队列
    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);
    
    //同步等待block的代码执行完
    dispatch_sync(_serialQueue, ^{ NSLog(@"1-----%@",[NSThread currentThread]);});
    
    NSLog(@"2-----%@",[NSThread currentThread]);
    
    dispatch_sync(_serialQueue, ^{ NSLog(@"3");});
    
    NSLog(@"4");
}

对于 dispatch_async 来说,把block提交到队列,立刻返回执行下一步(这里的立刻返回并不是计算好了结果,而是立刻返回的指令),并不会等待block执行完毕。所以它的打印结果有很多种,这取决于CPU的调度。譬如说 2413 或者 2143 或者 1234,但是1总在3前面,因为提交到的队列是 串行队列,打印3总在打印1后执行。

对于 dispatch_sync 来说,把block提交到队列,不立刻返回,它等待提交到队列的block执行完毕才继续向下执行,所以其执行结果只有一种: 1234,无论你运行多少次都会是这一种结果。

下面看队列使用的6种情况:


其中,将 任务 放入到 主队列 中 同步执行 是不可行的,会造成死锁。
★ 原因:主队列在执行dispatch_sync,函数会把一个block加入到指定的队列,此函数要求执行完block才返回,函数要求此时去执行block内容,但是主队列此时还在卡在函数的地方,函数线程还在,不能去执行block,也就是说函数和block是两个操作,在队列中前后关系。若是异步,函数添加完block就返回,顺序执行block内容,不存在死锁问题。

我将6种情况的代码列在下面,供大家参考,大家可以打印结果比对。(前三种是常用的,后三种不用考虑太多)

// dispatch_sync : 同步函数,不具备开启线程的能力。必须等待函数返回值返回,才会继续向下执行
// dispatch_async : 异步函数,具备开启线程的能力。并且不会等待函数返回值返回,而是继续向下执行操作
// 注意:上面提到的“函数返回值”指的是函数内部底层的一些操作,层层传递,层层返回。

#import "ViewController.h"
#import "WZYButton.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self asyncMainQueue];
    
    // 在子线程中执行(对应下面会产生死锁的方法syncMainQueue,在子线程中调用就不会死锁了)
//    [self performSelectorInBackground:@selector(syncMainQueue) withObject:nil];
}

/**
 *  async 异步函数 -- 并发队列(最常用)
 *  会不会创建线程:会,一般同时开多条
 *  任务的执行方式:并发执行
 */

// 因为会创建多条线程,彼此不影响,所以(start、end)输出正常
- (void)asyncGlobalQueue
{
    NSLog(@"---start---");
    // 获得全局的并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 将 任务 添加 全局队列 中去 并发 执行
    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]);
    });
    dispatch_async(queue, ^{
        NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
    });
    sleep(3);
    NSLog(@"---end---");
}

/**
 *  async 异步函数 -- 串行队列(有时候用)
 *  会不会创建线程:会,一般只开1条线程
 *  任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
 */

// 因为会创建一条子线程,彼此不影响,所以(start、end)输出正常
- (void)asyncSerialQueue
{
    NSLog(@"---start---");

    // 1.创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("cn.heima.queue", NULL);
    
    // 2.将 任务 添加到 串行队列 中去 串行 执行(主线程执行到dispatch后,然后需要开新线程去执行串行队列中的block,由于异步执行,那么二者互不耽误)
    dispatch_async(queue, ^{
        NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
    });
    
    for (int i = 0; i < 1000000000; i ++) {
        
    }
    NSLog(@"---end---");
    
    // 3.非ARC,需要释放创建的队列
    //    dispatch_release(queue);
}

/**
 *  async 异步函数 -- 主队列(很常用,比较特殊!此时的异步方式是无效的,不会开线程!所有任务的都在主线程中串行执行)
 */
- (void)asyncMainQueue
{
    NSLog(@"---start---");
    
    // 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.添加 任务 到主队列中 同步 执行(异步失效了,所以此刻还是同步)
    // 在执行过程中,整个asyncMainQueue就是在主线程中执行的。而执行到dispatch的时候,首先将NSLog“下载图片”操作扔进队列中去,然后添加到异步函数中。而NSLog“下载图片”需要等到主线程空闲的时候才执行(主队列会进行判断主线程是否空闲,空闲才会向下执行),又因为整个函数asyncMainQueue就是在主线程中进行的,它没执行完,主线程是不会空闲的。所以会让最后end输出之后,到达最后的右大括号再返回来输出NSLog“下载图片”
    dispatch_async(queue, ^{
        NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
    });
    
    for (int i = 0; i < 1000000000; i ++) {
        
    }
    NSLog(@"---end---");
}



/**-------------------------------------华丽的分割线------------------------------------------**/

/**
 *  sync 同步函数 -- 主队列(错误用法------会死锁,如果在子线程中调用下面的方法,是没有死锁的。这一点务必明白,必须让主线程空闲,才能这样去用)
 *  凡是放在主队列中的block都会在主线程中执行,主队列本身就是一个串行队列
 */
- (void)syncMainQueue
{
    NSLog(@"---start---");
    
    // 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.添加 任务 到主队列中 同步 执行(主线程执行syncMainQueue方法的过程中,当进行到执行同步函数这一步时。主队列在主线程中会进行一次判断,判定主线程是否在忙。如果主线程繁忙,没时间去接收NSLog这个操作,那么就会等待主线程空闲下来再去接收并执行操作。但是在同步函数中输出NSLog属于syncMainQueue方法内部的操作,而同步函数必须等待内部返回值返回之后才会继续向下进行,那么syncMainQueue永远不会执行完。所以两个操作互相等待,永远不会继续往下执行。导致死锁)
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
    });
    
    NSLog(@"---end---");
}

/**
 *  sync 同步函数 -- 并发队列
 *  会不会创建线程:不会
 *  任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
 *  并发队列失去了并发的功能
 */
- (void)syncGlobalQueue
{
    NSLog(@"---start---");
    // 获得全局的并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 将 任务 添加到 全局并发队列 中 同步 执行
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
    });
    
    NSLog(@"---end---");
}

/**
 *  sync 同步函数 -- 串行队列
 *  会不会创建线程:不会
 *  任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
 *  此方式和 sync -- 主队列 差不多,全程都是在主线程中完成,不同的是这个不会死锁,因为主队列相比于串行队列多了一个判断的功能,串行队列是不会观测主线程是否繁忙的。
 */
- (void)syncSerialQueue
{
    NSLog(@"start");
    
    // 创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("cn.heima.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
        NSLog(@"%@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        
        NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
    });
    
    NSLog(@"end");
}


@end




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值