iOS 多线程 之 GCD(大中枢派发)(一)

导语:

本文个人原创,转载请注明出处(http://www.cnblogs.com/pretty-guy/p/8126981.html)

在iOS开发中多线程操作通常是一下3种,本文着重介绍Dispatch

1. NSThread
2. NSOperation
3. GCD

先来一段Apple对dispath的介绍

Dispatch

Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.


GCD

The BSD subsystem, Core Foundation, and Cocoa APIs have all been extended to use these enhancements to help both the system and your application to run faster, more efficiently, and with improved responsiveness. Consider how difficult it is for a single application to use multiple cores effectively, let alone doing it on different computers with different numbers of computing cores or in an environment with multiple applications competing for those cores. GCD, operating at the system level, can better accommodate the needs of all running applications, matching them to the available system resources in a balanced fashion.

 

翻过过来就是:

Dispatch

通过提交工作来调度系统管理的调度队列,在多核硬件上并发的执行代码

GCD
BSD子系统,Core Foundation和Cocoa API已经全部扩展到使用这些增强功能来帮助系统和应用程序更快,更高效地运行,并且提高了响应速度。 考虑单个应用程序有效地使用多个内核是多么的困难,更不用说在具有不同数量的计算核心的不同计算机上或者在具有多个应用程序的环境中竞争这些内核。 在系统级运行的GCD可以更好地适应所有正在运行的应用程序的需求,并以均衡的方式将它们与可用的系统资源进行匹配。

划重点:在系统级运行的GCD可以更好地适应所有正在运行的应用程序的需求,并以均衡的方式将它们与可用的系统资源进行匹配。意思很明显,多线程操作还是建议使用GCD,毕竟是系统级别的😂。

一、Dispatch queue介绍

dispatch queue 主要有两种类型:

1. DISPATCH_QUEUE_SERIAL \\线行队列
2. DISPATCH_QUEUE_CONCURRENT \\并行队列

在队列中执行任务有两种方式,一种是异步执行,一种是同步执行,方式如下

1. dispatch_async(dispatch_queue_t queue, dispatch_block_t block);//异步执行
2. dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block); //同步执行

这两个方法都不会阻塞当前线程

二、队列的基本操作

1. 创建队列

创建队列的方法是

dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);

此方法创建的队列按照FIFO调用block,其中线性队列同时只会调用一个,并发队列可以同时执行多个任务。

其中,label是队列名,attr指定队列类型为 DISPATCH_QUEUE_SERIAL 或 DISPATCH_QUEUE_CONCURRENT。

dispatch_queue_t serialQueue = dispatch_queue_create("com.wk.test.serialQueue", DISPATCH_QUEUE_SERIAL);//串行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.wk.test.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);//并行队列

除了使用dispatch_queue_create创建队列还可以获取系统定义的全局队列,并且制定优先级

dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags)

global_quque也是按照FIFO来调用block,可以同时执行多个任务

2.调用队列

调用队列也有两种方式

dispatch_async(dispatch_queue_t queue, dispatch_block_t block) //dispatch_async 异步执行,不会阻塞当前线程
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block)//dispatch_sync 同步执行,会阻塞当前线程

dispatch_async 异步执行block,结果是串行还是并行执行由queue的类型来决定。

dispatch_sync 同步执行,会阻塞当前线程,同时作为优化,如果当前线程是串行队列的话,会直接在当前队列执行。也就是说如果在主线程使用dispatch_sync来调用队列的话,block会在主线程中执行,而不会在新建的队列中执行。

了解了这么多,来思考一下以下代码的执行结果会是什么?

2.1 串行队列示例

dispatch_queue_t serialQueue = dispatch_queue_create("com.wk.test.serialQueue", DISPATCH_QUEUE_SERIAL);//串行队列

    NSLog(@"dispatch_async ===========");

    

    

    dispatch_async(serialQueue, ^{

        NSLog(@"dispatch_async task 1 begin %@", [NSThread currentThread]);

        sleep(1);

        NSLog(@"dispatch_async task 1 end");

    });

    

    dispatch_async(serialQueue, ^{

        NSLog(@"dispatch_async task 2 begin %@", [NSThread currentThread]);

        sleep(1);

        NSLog(@"dispatch_async task 2 end");

    });

    

    dispatch_async(serialQueue, ^{

        NSLog(@"dispatch_async task 3 begin %@", [NSThread currentThread]);

        sleep(1);

        NSLog(@"dispatch_async task 3 end");

    });

    

    dispatch_async(serialQueue, ^{

        NSLog(@"dispatch_async task 4 begin %@", [NSThread currentThread]);

        sleep(1);

        NSLog(@"dispatch_async task 4 end");

    });

    

    dispatch_async(serialQueue, ^{

        NSLog(@"dispatch_async task 5 begin %@", [NSThread currentThread]);

        sleep(1);

        NSLog(@"dispatch_async task 5 end");

    });

    

    

    

    NSLog(@"dispatch_sync ===========");

    dispatch_sync(serialQueue, ^{

        NSLog(@"dispatch_sync task 1 begin %@", [NSThread currentThread]);

        sleep(0.1);

        NSLog(@"dispatch_sync task 1 end");

    });

    

    dispatch_sync(serialQueue, ^{

        NSLog(@"dispatch_sync task 2 begin %@", [NSThread currentThread]);

        sleep(0.1);

        NSLog(@"dispatch_sync task 2 end");

    });

    

    dispatch_sync(serialQueue, ^{

        

        NSLog(@"dispatch_sync task 3 begin %@", [NSThread currentThread]);

        sleep(0.1);

        NSLog(@"dispatch_sync task 3 end");

    });

    

    dispatch_sync(serialQueue, ^{

        NSLog(@"dispatch_sync task 4 begin %@", [NSThread currentThread]);

        sleep(0.1);

        NSLog(@"dispatch_sync task 4 end");

    });

    

    dispatch_sync(serialQueue, ^{

        NSLog(@"dispatch_sync task 5 begin %@", [NSThread currentThread]);

        sleep(0.1);

        NSLog(@"dispatch_sync task 5 end");

    });

以上代码的执行结果

2.2 异步调用concurrent queue示例

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.wk.test.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);//并行队列

    NSLog(@"dispatch_async调用concurrent queue=================");

    dispatch_async(concurrentQueue, ^{

        NSLog(@"task 1 begin %@", [NSThread currentThread]);

        sleep(1.f);

        NSLog(@"task 1 end");

    });

    

    dispatch_async(concurrentQueue, ^{

        NSLog(@"task 2 begin %@", [NSThread currentThread]);

        sleep(1.f);

        NSLog(@"task 2 end");

    });

    

    dispatch_async(concurrentQueue, ^{

        NSLog(@"task 3 begin %@", [NSThread currentThread]);

        sleep(1.f);

        NSLog(@"task 3 end");

    });

    

    dispatch_async(concurrentQueue, ^{

        NSLog(@"task 4 begin %@", [NSThread currentThread]);

        sleep(1.f);

        NSLog(@"task 4 end");

    });

    

    dispatch_async(concurrentQueue, ^{

        NSLog(@"task 5 begin %@", [NSThread currentThread]);

        sleep(1.f);

        NSLog(@"task 5 end");

    });

结果:

2.3 同步调用concurrent queue

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.wk.test.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);//并行队列
    NSLog(@"dispatch_async调用concurrent queue=================");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task 1 begin %@", [NSThread currentThread]);
        sleep(1.f);
        NSLog(@"task 1 end");
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task 2 begin %@", [NSThread currentThread]);
        sleep(1.f);
        NSLog(@"task 2 end");
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task 3 begin %@", [NSThread currentThread]);
        sleep(1.f);
        NSLog(@"task 3 end");
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task 4 begin %@", [NSThread currentThread]);
        sleep(1.f);
        NSLog(@"task 4 end");
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task 5 begin %@", [NSThread currentThread]);
        sleep(1.f);
        NSLog(@"task 5 end");
    });

运行结果:

3.Dispatch group 使用

在实际的项目开发中会遇到这样的情况,需要多个任务并发执行,在这多个任务同时结束之后进行下一项操作,常规的做法可以做一个计数count,每个任务结束后count--,在每个任务完成的时候检查任务是否全部完成,在执行下一项操作。使用dispath_group_t就会简单很多

3.1 dispatch_queue_t 基本使用

//创建group
    dispatch_group_t group = dispatch_group_create();
    //创建全局队列
    dispatch_queue_t globleQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
    
    //往group中天的要执行的block以及执行的队列
    dispatch_group_async(group, globleQueue, ^{
        NSLog(@"task 1 begin");
        sleep(3.f);
        NSLog(@"task 1 end");
    });
    
    dispatch_group_async(group, globleQueue, ^{
        NSLog(@"task 2 begin");
        sleep(2.f);
        NSLog(@"task 2 end");
    });
    
    dispatch_group_async(group, globleQueue, ^{
        NSLog(@"task 3 begin");
        sleep(1.f);
        NSLog(@"task 3 end");
    });
    
    dispatch_group_async(group, globleQueue, ^{
        NSLog(@"task 4 begin");
        sleep(2.f);
        NSLog(@"task 4 end");
    });
    
    //设置超时时间,会阻塞当前线程,直到block全部执行或者超时
    NSInteger success = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    //添加任务执行结束处理
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSString *result = success == 0 ? @"task all done" : @"mission failed";
        NSLog(@"%@", result);
    });

3.2 dispatch_group_t 的进阶使用

上面的情况只适用于分步操作里为同步操作的情况,当每一步的操作是异步操作例如网络请求时,像上面这样的做法就不行了,这时候就需要我们手动的操作group的进入和离开,这里要用到两个新的api dispatch_group_enter与dispatch_group_leave,这两个api必须成对出现

dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t globleQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
    dispatch_queue_t subGlobleQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
    //模拟网络请求
    dispatch_group_enter(group);
    dispatch_async(globleQueue, ^{
        NSLog(@"task 1 begin");
        dispatch_async(subGlobleQueue, ^{
            sleep(1);
            NSLog(@"task 1 end");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_enter(group);
    dispatch_async(globleQueue, ^{
        NSLog(@"task 2 begin");
        dispatch_async(subGlobleQueue, ^{
            sleep(1);
            NSLog(@"task 2 end");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_enter(group);
    dispatch_async(globleQueue, ^{
        NSLog(@"task 3 begin");
        dispatch_async(subGlobleQueue, ^{
            sleep(3);
            NSLog(@"task 3 end");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"task all done");
    });

4. 信号量 dispatch_semaphore_t 的使用

现在抛出一个问题:如果在多线程的操作中,如果需要限制最大并发数应该怎么做呢?是不是觉得有点伤脑经,庆幸的是系统提供了信号量来做这个事情。代码如下:

//信号量可以用来控制并发操作的最大并发数或者最大的资源访问数,当最大并发数设置为1的时候,就可以让并发操作变成线行操作
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
    
    dispatch_queue_t globleQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
    
    dispatch_async(globleQueue, ^{
        for (int i = 0; i < 10; i++) {
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            //模拟异步操作
            int temp = i;
            dispatch_async(globleQueue, ^{
                NSLog(@"task %i begin", temp);
                sleep(1.f);
                dispatch_semaphore_signal(semaphore);
            });
        }
    });

在创建信号量的时候指定一个初始值,调用dispatch_semaphore_wait会让信号量-1,调用dispatch_semaphore_signal会让信号量+1,每次调用dispatch_semaphore_wait的时候会对当前的计数值进行检查,如果当前信号量<=0的话,就会阻塞当前线程,直到有dispatch_semaphore_signal调用时才会继续往后执行。

5.dispatch_barrier栅栏的使用

栅栏分为dispatch_barrier_sync与dispatch_barrier_async,方法如下:

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);

dispatch_barrier_async与dispatch_barrier_sync的相同点:block会在指定queue执行完当前的blocks之后执行,在dispatch_barrier之后添加的block要等到栅栏执行完之后才执行。区别就是:前者不会阻塞当前线程,后者会阻塞当前线程

使用栅栏的时候需要注意 只接受用户自己创建的concurrent_queue。也就是如下方式创建的:

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.wk.barroierQueue", DISPATCH_QUEUE_CONCURRENT);

如果传入的是serial_queue或者globle_queue的话,栅栏就会没有效果,执行效果会想相当于调用 dispatch_async与 dispatch_sync。

以下为示例代码:

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.wk.barroierQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_queue_t globleQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
    
    dispatch_async(globleQueue, ^{
        
        dispatch_async(concurrentQueue, ^{
            NSLog(@"task queue 1 begin");
            sleep(1);
            NSLog(@"task queue 1 end");
        });
        
        dispatch_async(concurrentQueue, ^{
            NSLog(@"task queue 2 begin");
            sleep(1);
            NSLog(@"task queue 2 end");
        });
        
        dispatch_barrier_async(concurrentQueue, ^{
            NSLog(@"dispatch_barrier_sync task 1 begin %@", [NSThread currentThread]);
            sleep(1);
            NSLog(@"dispatch_barrier_sync task 1 end");
        });
        
        dispatch_async(concurrentQueue, ^{
            NSLog(@"task queue 3 begin");
            sleep(1);
            NSLog(@"task queue 3 end");
        });

    });

你可以在这里下载demo

以上就是GCD的大部分操作了,剩下的部分下一篇博客见。

 本文个人原创,转载请注明出处(http://www.cnblogs.com/pretty-guy/p/8126981.html)

posted @ 2017-12-27 17:51  pretty guy  阅读(609)  评论(0编辑  收藏  举报