iOS Learning

GCD(Grand Central Dispatch)快速上手

2016-02-26  本文已影响209人  炒鸡范

一、先来看一个例子

-(void)testSerialQueue
{
    dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
    for (NSInteger i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%ld",i);
    });
}

dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL)创建了一个dispatch_queue_t类型的对象,其中:
"myqueue"是这个queue的标示符,随便输入个字符串就好了·
DISPATCH_QUEUE_SERIAL标示创建的是一个串行队列。那么并列的还有DISPATCH_QUEUE_CONCURRENT并行队列,会在下面的例子中看到这两者的区别。
dispatch_async()有两个参数,参数1是一个queue,这里把我们刚创建的queue写上去,参数2是一个block,block中写我们需要多线程执行的代码。
那么dispatch_async()的意思是通过async也就是异步的方式将block中的代码提交到参数1中的queue队列中去。

我们执行这个函数,可以看到:

//    2016-02-25 21:03:57.234 FL_GCD_DEMO[4270:3695426] 0
//    2016-02-25 21:03:57.248 FL_GCD_DEMO[4270:3695426] 1
//    2016-02-25 21:03:57.266 FL_GCD_DEMO[4270:3695426] 2
//    2016-02-25 21:03:57.268 FL_GCD_DEMO[4270:3695426] 3
//    2016-02-25 21:03:57.269 FL_GCD_DEMO[4270:3695426] 4
//    2016-02-25 21:03:57.269 FL_GCD_DEMO[4270:3695426] 5
//    2016-02-25 21:03:57.270 FL_GCD_DEMO[4270:3695426] 6
//    2016-02-25 21:03:57.271 FL_GCD_DEMO[4270:3695426] 7
//    2016-02-25 21:03:57.272 FL_GCD_DEMO[4270:3695426] 8
//    2016-02-25 21:03:57.272 FL_GCD_DEMO[4270:3695426] 9

输出是有序的,这好像和我们平时的操作并没有什么区别。
接下来我们尝试使用DISPATCH_QUEUE_CONCURRENT

-(void)testConcurrentQueue
{
    dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
    for (NSInteger i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%ld",i);
        });
    }
}

我们只是更改了create的第二个参数为DISPATCH_QUEUE_CONCURRENT,这将使创建的队列类型为并发队列。
执行以上代码,输出结果如下:

//    2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696674] 2
//    2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696672] 0
//    2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696673] 1
//    2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696684] 3
//    2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696685] 4
//    2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696686] 5
//    2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696674] 6
//    2016-02-25 21:06:04.794 FL_GCD_DEMO[4307:3696672] 7
//    2016-02-25 21:06:04.794 FL_GCD_DEMO[4307:3696673] 8
//    2016-02-25 21:06:04.794 FL_GCD_DEMO[4307:3696684] 9

这个时候我们可以看到输的结果是无序的。这是因为并发队列尽管遵循FIFO(先进先出)的执行顺序,但是由于队列是并发的,我们无法决定代码的具体执行时间。

从上述两个方法我们可以看出DISPATCH_QUEUE_SERIALDISPATCH_QUEUE_CONCURRENT的区别,他们分别创建的是串行和并发队列

二、来一些经常使用的

有时候我们并不想每次都做创建的动作,而系统也为我们提供了一系列现成的队列供我们使用,我们可以通过以下代码来获得:

dispatch_get_main_queue();
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

这两个函数会分别返回一个"main_queue"和"global_queue",其中main_queue是系统的主线程,主要用来执行界面更新的操作。而global_queue则是系统的提供的全局线程,我们应当尽量将复杂计算的操作放到global_queue上,如果放在main_queue上则会阻塞主线程,直观的反应就是使界面变卡。
这两个queue的相关测试代码就不写了。

三、dispatch_group

group是什么?顾名思义是“组”的意思。
有时候我们会碰到一些关联的操作,这些关联的操作共同来完成一件事情。比如用户登录的时候需要完成填写账号、填写密码两件事情,才能执行登录操作(当然用户系统肯定不是这么来的...),我们用代码体现下:

-(void)testGroupQueue
{
        NSLog(@"用户想要登录");

        dispatch_group_t group = dispatch_group_create();
        
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            sleep(1);
            NSLog(@"填写账号完成");
        });
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            sleep(1);
            NSLog(@"填写密码完成");
        });

        dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSLog(@"用户可以登录了");
        });
}

执行以上代码,得到的结果如下:

    2016-02-26 13:22:09.973 FL_GCD_DEMO[5494:3812823] 用户想要登录
    2016-02-26 13:22:10.978 FL_GCD_DEMO[5494:3812859] 填写密码完成
    2016-02-26 13:22:10.978 FL_GCD_DEMO[5494:3812860] 填写账号完成
    2016-02-26 13:22:10.978 FL_GCD_DEMO[5494:3812860] 用户可以登录了

可以看到,用户“填写账号完成”和“填写密码完成”后,才执行“用户可以登录了”
以上代码中,dispatch_group_create()创建了一个dispatch_group_t类型的对象group,然后我们把需要放入组操作通过dispatch_group_async()放到group中,这个函数有三个参数:
参数1:要放入的组对象
参数2:参数3需要放入的队列queue
参数3:需要执行的操作如:NSLog(@"填写密码完成");
我们可以向这个group中放入多个操作。
dispatch_group_notify()这个函数会在以上放入的所有操作都执行完成后,调用此函数中的参数3操作:NSLog(@"用户可以登录了");

四、dispatch_apply

apply一般会用来异步遍历操作来提高工作效率。

-(void)testApply
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    NSArray *array = @[@1,@2,@3,@4,@5,@6,@6];
    size_t count = array.count;
    dispatch_apply(count, queue, ^(size_t i) {
        NSLog(@"%@",array[i]);
    });
}

以上代码的执行结果如下:

    2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826232] 1
    2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826274] 2
    2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826273] 3
    2016-02-26 13:37:57.648 FL_GCD_DEMO[5555:3826232] 5
    2016-02-26 13:37:57.648 FL_GCD_DEMO[5555:3826274] 6
    2016-02-26 13:37:57.648 FL_GCD_DEMO[5555:3826273] 6
    2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826275] 4

需要注意的是apply会阻塞主线程,如果涉及到大量的数据操作不建议使用apply在主线程上。

五、dispatch_after

dispatch_after用来延后将操作放入队列

-(void)testAfter
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    NSLog(@"开始");
    dispatch_async(queue, ^{
        NSLog(@"我先来");
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"我等等");
    });
}

执行结果如下:

    2016-02-26 13:40:57.665 FL_GCD_DEMO[5611:3829303] 开始
    2016-02-26 13:40:57.665 FL_GCD_DEMO[5611:3829339] 我先来
    2016-02-26 13:41:00.666 FL_GCD_DEMO[5611:3829339] 我等等

六、Suspend和Resume

有时可能需要我们中断某些操作,还需要我们恢复执行,这个时候就可以用到suspend和resume操作:

-(void)testSuspendAndResume
{
    dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"start");
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:5];
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:5];
        NSLog(@"2");
    });
    
    [NSThread sleepForTimeInterval:3];
    NSLog(@"suspend");
    dispatch_suspend(queue);
    [NSThread sleepForTimeInterval:5];
    NSLog(@"resume");
    dispatch_resume(queue);
}

以上代码执行结果如下:

    2016-02-25 22:01:02.475 FL_GCD_DEMO[4890:3722720] start
    2016-02-25 22:01:05.476 FL_GCD_DEMO[4890:3722720] suspend
    2016-02-25 22:01:07.479 FL_GCD_DEMO[4890:3722759] 1
    2016-02-25 22:01:10.478 FL_GCD_DEMO[4890:3722720] resume
    2016-02-25 22:01:15.478 FL_GCD_DEMO[4890:3722759] 2

需要注意的是,suspend不会停止当当前正在执行的block,而是中断了后面未执行的,在resume之后再执行后面的block。

七、dispatch_barrier

barrier从字面理解就是障碍物的意思,实际他的功能也是障碍物。
barrier会按照它被加入队列的顺序,隔断前后的操作,使在它之前加入的任务先执行,然后执行barrier,再执行在它之后加入的任务:

-(void)testBarrier
{
    dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"我在这里隔断了");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"4");
    });
}

以上代码的执行结果如下:

    2016-02-25 22:13:28.454 FL_GCD_DEMO[5030:3728945] 2
    2016-02-25 22:13:28.454 FL_GCD_DEMO[5030:3728942] 1
    2016-02-25 22:13:28.455 FL_GCD_DEMO[5030:3728942] 我在这里隔断了
    2016-02-25 22:13:28.455 FL_GCD_DEMO[5030:3728945] 4
    2016-02-25 22:13:28.455 FL_GCD_DEMO[5030:3728942] 3

八、dispatch_semaphore

semaphore是信号的意思。在我们的当前系统下资源并不是无限的,我们需要控制资源的使用例如控制线程的数量。实际生活中的例子比如下载,我们需要控制允许同时下载的数量。

-(void)testSemaphore
{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for (int i = 0; i <10; i++)
    {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_async(queue, ^{
            NSLog(@"下载资源:%i",i);
            sleep(2);
            dispatch_semaphore_signal(semaphore);
        });
    }
}

运行结果如下:

    2016-02-26 14:04:33.392 FL_GCD_DEMO[5713:3848434] 下载资源:1
    2016-02-26 14:04:33.392 FL_GCD_DEMO[5713:3848431] 下载资源:0
    2016-02-26 14:04:35.397 FL_GCD_DEMO[5713:3848434] 下载资源:2
    2016-02-26 14:04:35.397 FL_GCD_DEMO[5713:3848431] 下载资源:3
    2016-02-26 14:04:37.400 FL_GCD_DEMO[5713:3848434] 下载资源:5
    2016-02-26 14:04:37.400 FL_GCD_DEMO[5713:3848431] 下载资源:4
    2016-02-26 14:04:39.401 FL_GCD_DEMO[5713:3848431] 下载资源:6
    2016-02-26 14:04:39.401 FL_GCD_DEMO[5713:3848434] 下载资源:7
    2016-02-26 14:04:41.406 FL_GCD_DEMO[5713:3848434] 下载资源:9
    2016-02-26 14:04:41.406 FL_GCD_DEMO[5713:3848431] 下载资源:8

我们可以这么理解:
dispatch_semaphore_create(2)创建了一个单位为2的信号量
dispatch_semaphore_wait()会消耗一个单位
ispatch_semaphore_signal()会增加一个单位
dispatch_semaphore_create(2)中传入的“2”决定了这个信号量最多包含2个单位。
dispatch_semaphore_wait()中的参数2DISPATCH_TIME_FOREVER表示当信号量单位变为0个,则使得代码的执行停止在当前位置不再继续下去,直到收到signal使得单位大于0,才继续往下执行。
在上述代码中,每执行一次下载会占用一个单位,下载完成后通过signal告诉wait可以继续向下执行代码,从而控制同时进行的下载数量。

以上代码github
https://github.com/Leovir/FL_GCD_DEMO

上一篇下一篇

猜你喜欢

热点阅读