美文网首页
iOS多线程之GCD

iOS多线程之GCD

作者: 和女神经常玩 | 来源:发表于2018-04-06 17:26 被阅读0次

很久前的总结,今天贴出来。适合看了就用,很少讲解,纯粹用法。

目录

Dispatch Queue

dispatch_queue_create

系统标准提供的Dispatch Queue

dispatch_set_target_queue

dispatch_after

Dispatch Group

dispatch_barrier_async

dispatch_sync

dispatch_apply

dispatch_suspend和dispatch_resume

Dispatch Semaphore

dispatch_once


Dsipatch Queue

官方说明:开发者要做的只是定义想要执行的任务并追加到适当的Dispatch Queue中。

    这句话用如下代码表示:

    dispatch_async(queue, ^{//想要执行的任务});

    即:使用block“定义想要执行的任务”,通过dispatch_async函数“追加”到赋值在queue的“Dispatch Queue”中,这样既可使指定的block在另一线程中运行。

简介:Dispatch Queue 是执行处理的等待队列。通过dispatch_async函数等API,在block中记述想要执行的处理并将其追加到Dispatch Queue中。Dispatch Queue按照追加的顺序执行处理。

分类:

    Serial Dispatch Queue:等待现在执行中的处理。

    Concurrent Dispatch Queue:不等待现在执行中的处理。

比较:

    Serial Dispatch Queue:因为等待现在执行中的处理结束,所以后一个等待前一个处理执行结束后才开始执行,同时执行的处理数只有一个。

    Concurrent Dispatch Queue:因为不等待现在执行中的处理结束,所以由前到后依次开始执行,即并发(使用多个线程同时)执行多个处理。注:并发执行的处理数取决于当前系统的状态。

附:

iOS和OSX的核心一一XNU内核决定应当使用的线程数,并只生成所需的线程执行处理。另外,当执行结束,应当执行的处理减少时,XNU内核会结束不再需要的线程。XNU内核仅使用Concurrent Dispatch Queue 便可完美的管理并执行多个处理的线程。


dispatch_queue_create

//虽然Serial Dispatch Queue和Concurrent Dispatch Queue受到系统资源的限制,但用dispatch_queue_create函数可生成任意多个Dispatch Queue。

    //在一个Serial Dispatch Queue中同时只能执行一个追加处理,多个Serial Dispatch Queue可并行执行

    //过多使用Serial Dispatch Queue,会消耗大量内存,引起大量上下文切换,大幅度降低系统的响应性能,因此注意Serial Dispatch Queue的生成个数。

    //Concurrent Dispatch Queue不管生成多少,由于XNU内核只使用有效管理的线程,因此不会发生Serial Dispatch Queue的问题。

    //多个线程更新相同资源导致的数据竞问题,使用Serial Dispatch Queue。

    //当并行执行不发生数据竞争等问题的处理时,使用Concurrent Dispatch Queue。

    //通过dispatch_queue_create可生成Dispatch Queue,注意,生成的Dispatch Queue在使用结束后必须由程序员进行释放(现在GCD对象已经纳入了ARC的管理范围,在纳入之前是需要程序员按照内存管理的思考方式来手动管理GCD对象的)。

    dispatch_queue_t serialDispatchQueue = dispatch_queue_create("Dispatch Queue的名称", NULL);

    //参数1:Dispatch Queue的名称,可设NULL

    //参数2:指定Dispatch Queue的类型。若为NULL,则为Serial Dispatch Queue;若为DISPATCH_QUEUE_CONCURRENT,则为Concurrent Dispatch Queue

    //返回值:表示Dispatch Queue的diapatch_queue_t类型

    //通过dispatch_release函数释放

    //dispatch_release(serialDispatchQueue);

    //通过dispatch_retain函数持有

    //dispatch_retain(serialDispatchQueue);

    //通过dispatch_async函数将block中定义的处理“追加”到赋值在serialDispatchQueue的“Dispatch Queue”中

    dispatch_async(serialDispatchQueue, ^{

        NSLog(@"//被追加的定义处理的block");

    });

    //注意,在dispatch_async函数中追加Block到Dispatch Queue,该Block通过dispatch_retain函数持有Dispatch Queue。无论Dispatch Queue是Serial Dispatch Queue还是Concurrent Dispatch Queue都一样。一旦Block执行结束,就通过dispatch_release函数释放该Block持有的Dispatch Queue。


系统标准提供的Dispatch Queue

//Main Dispatch Queue,是在主线程中执行的Dispatch Queue。Main Dispatch Queue是Serial Dispatch Queue。追加到Main Dispatch Queue的处理在主线程的RunLoop中执行。由于Main Dispatch Queue在主线程中执行,因此要将用户界面的界面更新等一些必须在主线程中执行的处理追加到Main Dispatch Queue使用。

    //Global Dispatch Queue,是所有的应用程序都能够使用的Concurrent Dispatch Queue。没有必要通过dispatch_queue_create函数逐个生成Concurrent Dispatch Queue。只要获取Global Dispatch Queue即可。Global Dispatch Queue有4个执行优先级,分别是高优先级(High Priority)、默认优先级(Default Priority)、低优先级(Low Priority)和后台优先级(Background Priority)。用于Global Dispatch Queue的线程的执行优先级,使用Global Dispatch Queue的执行优先级。线程的执行优先级不能保证实时性,只是大致的判断。

    //Main Dispatch Queue的获取方法

    dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

    //Global Dispatch Queue(高优先级)的获取方法

    dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    //Global Dispatch Queue(默认优先级)的获取方法

    dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //Global Dispatch Queue(低优先级)的获取方法

    dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW , 0);

    //Global Dispatch Queue(后台优先级)的获取方法

    dispatch_queue_t globalDispatchQueueBackgroud = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND , 0);

    //另外,对于Main Dispatch Queue和Global Dispatch Queue执行dispatch_release函数和dispatch和_retain函数不会引起任何变化,也不会有任何问题。但为了符合内存管理的思考方式,可以直接对Main Dispatch Queue和Global Dispatch Queue执行dispatch_release函数和dispatch和_retain函数。


dispatch_set_target_queue

//dispatch_queue_create函数生成的Dispatch Queue不管是Serial Dispatch Queue还是Concurrent Dispatch Queue,都使用与默认优先级Global Dispatch Queue相同执行优先级的线程。

    //通过dispatch_set_target_queue函数变更生成的Dispatch Queue的优先级。

    dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("Dispatch Queue的名称", NULL);

    dispatch_queue_t globalDispatchQueueBackgroud = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND , 0);

    dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackgroud);

    //参数1:指定要变更执行优先级的Dispatch Queue

    //参数2:指定与要变更为的执行优先级相同的Dispatch Queue,即目标。

    //注:第一个参数不能指定为系统标准提供的Dispatch Queue。

    //通过dispatch_set_target_queue防止处理并行执行

    //如果在多个Serial Dispatch Queue中用dispatch_set_target_queue函数指定目标为某一个Serial Dispatch Queue,那么原先本应并行执行的多个Serial Dispatch Queue,在目标Serial Dispatch Queue上只能同时执行一个处理。


dispatch_after

//想在指定时间后执行处理的情况,可使用dispatch_after函数来实现。

    //dispatch_after函数并不是在指定的时间后执行处理,而只是在指定的时间追加处理到Dispatch Queue。

    //通过dispatch_after在3秒后将指定的block追加到Main Dispatch Queue

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ull*NSEC_PER_SEC);

    dispatch_after(time, dispatch_get_main_queue(), ^{

        NSLog(@"1秒后");

    });

    //参数1:指定时间用的dispatch_time_t类型的值,该值使用dispatch_time函数或dispatch_walltime函数作成。

    //参数2:指定要追加处理的Dispatch Queue

    //参数3:指定记述要执行处理的Block

    //注:因为Main Dispatch Queue在主线程的RunLoop中执行,所以在比如每隔1/60秒执行的RunLoop中,Block最快在3秒后执行,最慢在3秒+1/60秒后执行,并且在Main Dispatch Queue有大量处理追加或主线程的处理本身有延迟时,这个时间会更长。虽然在有严格时间的要求下使用时会出现问题,但在想大致延迟执行处理时,该函数是非常有效的。

    //dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ull*NSEC_PER_SEC);

    //dispatch_time函数能够获取从第一个参数dispatch_time_t类型值中指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。DISPATCH_TIME_NOW表示现在的时间。第二个参数中用秒和NSEC_PER_SEC的相乘来表示,毫秒和NSEC_PER_MSEC相乘来表示。ull即unsigned long long

    //dispatch_walltime函数用于计算绝对时间,如某个具体的时间。而dispatch_time通常用于计算相对时间。


Dispatch Group

//在追加到Dispatch Queue中的多个处理全部结束后想执行结束处理时:

    //a.只使用一个Serial Dispatch Queue时,只要将想执行的处理全部追加到该 Serial Dispatch Queue中并在最后追加结束处理,即可实现。

    //b.使用Concurrent Dispatch Queue时或同时使用多个Dispatch Queue时,可通过Dispatch Group实现。

    //无论向什么样的Dispatch Queue中追加处理,使用Dispatch Group都可监视这些处理执行的结果。一旦检测到所有处理执行结束,就可将结束的处理追加到Dispatch Queue中。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //通过dispatch_group_create函数生成Dispatch Group,注意,生成的Dispatch Group在使用结束后必须通过dispatch_release释放(现在GCD对象已经纳入了ARC的管理范围,在纳入之前是需要程序员按照内存管理的思考方式来手动管理GCD对象的),同Dispatch Queue。

    dispatch_group_t group = dispatch_group_create();

    //通过dispatch_group_async函数追加block到指定的Dispatch Queue中,第一个参数为监视的Dispatch Group。

    //注:与追加Block到Dispatch Queue时同样,Block通过dispatch_retain函数持有Dispatch Group,如果Block执行结束,该Block就通过dispatch_release函数释放持有的Dispatch Group。一旦Dispatch Group使用结束,不用考虑属于该Dispatch Group的Block,立即通过dispatch_release函数释放即可。

    dispatch_group_async(group, queue, ^{

        NSLog(@"blk0");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"blk1");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"blk2");

    });

    //通过dispatch_group_notify函数,在追加到Dispatch Queue中(即被Dispatch Group监测)的处理全部执行结束时,会将执行结束处理的Block追加到Dispatch Queue中

    //参数1:指定为要监视的Dispatch Group。

    //参数2:指定要追加到的Dispatch Queue。

    //参数3:结束处理的block

    dispatch_group_notify(group, queue, ^{

        NSLog(@"结束处理");

    });

    //也可以使用dispatch_group_wait函数仅等待全部处理执行结束

    //等待:意味着一旦调用dispatch_group_wait函数,该函数就处于调用的状态而不返回。执行dispatch_group_wait函数的现在的线程(当前线程)停止。在经过dispatch_group_wait函数中指定的时间或属于指定Dispatch Group的处理全部执行结束之前,执行该函数的线程停止。

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    //参数1:指定Dispatch Group

    //参数2:指定最多等待的时间(超时),dispatch_time_t类型值

    //返回值:若等待的全部处理执行结束,返回0。否则,返回不为0。

    //当参数2为指定等待1秒时:

    dispatch_time_t time2 = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);

    long result = dispatch_group_wait(group, time2);

    if (result == 0) {

        //若全部处理执行结束,返回值为0

    }else{

        //若超时,即还有处理在执行,则返回值不为0

    }

    //当参数2为DISPATCH_TIME_FORVER,意味着永久等待。只要属于Dispatch Group的处理尚未执行结束,就会一直等待,中途不能取消。因此dispatch_group_wait函数返回时,由于属于Dispatch Group的处理必定全部执行结束,因此返回值为0。

    //当参数2为DISPATCH_TIME_NOW,则不用任何等待即可判定属于Dispatch Group的处理是否执行结束


dispatch_barrier_async

//写入处理不可以与写入处理和读取处理并行执行。读取处理与读取处理可并行执行

    //为了高效率的进行访问:

    //读取处理追加到Concurrent Dispatch Queue中,写入处理在任一个读取处理没有执行的状态下,追加到Serial Dispatch Queue中即可,在写入处理结束之前,读取处理不可执行。

    //通过dispatch_barrier_async也可实现

    dispatch_queue_t queue = dispatch_queue_create("Dispatch Queue的名称", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{/*读取处理0*/});

    dispatch_async(queue, ^{/*读取处理1*/});

    dispatch_async(queue, ^{/*读取处理2*/});

    dispatch_async(queue, ^{/*读取处理3*/});

    //写入处理,之后会对写入的内容进行读取处理

  //若简单的在dispatch_async函数中加入写入处理,那么根据Concurrent Dispatch Queue的性质,就有可能在追加到写入处理前面的处理中读取到与期待不符的数据,还可能因非法访问导致应用程序异常结束。如果追加多个写入处理,则可能发生更多问题,比如数据竞争。

    //通过dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再将指定的处理(异步)追加到该Concurrent Dispatch Queue中。然后在由dispatch_barrier_async函数追加的处理执行完毕后,Concurrent Dispatch Queue才恢复为一般的动作,追加到该Concurrent Dispatch Queue的处理又开始并行执行。

    dispatch_barrier_async(queue, ^{/*写入处理*/});

    dispatch_async(queue, ^{/*读取处理4*/});

    dispatch_async(queue, ^{/*读取处理5*/});

    dispatch_async(queue, ^{/*读取处理6*/});

    dispatch_async(queue, ^{/*读取处理7*/});

    //dispatch_barrier_sync函数与dispatch_barrier_async不同的地方是它会将指定的block同步追加到Concurrent Dispatch Queue中。在追加Block结束之前,dispatch_barrier_sync函数会一直等待。即dispatch_barrier_async不阻塞当前线程,dispatch_barrier_sync阻塞当前线程。

参考资料:http://blog.csdn.net/u013046795/article/details/47057585


dispatch_sync

//dispatch_async函数将指定的Block"非同步"的追加到指定的Dispatch Queue中,dispatch_async函数不作任何等待。

    //dispatch_sync函数将指定的Block"同步"追加到指定的Dispatch Queue中。在追加Block结束之前,dispatch_sync函数会一直等待。即一旦调用dispatch_sync函数,那么在指定的处理执行结束之前,该函数不会返回。

    //dispatch_sync函数出现的死锁问题:

    //a.该源代码在Main Dispatch Queue即主线程中执行指定的Block,并等待其执行结束。而其实主线程中正在执行这些源代码,所以无法执行追加到Main Dispatch Queue 的Block。

    dispatch_queue_t queue = dispatch_get_main_queue();

    dispatch_sync(queue, ^{

        NSLog(@"hello");

    });

    //b.Main Dispatch Queue中执行的Block等待Main Dispatch Queue中要执行的Block执行结束

    dispatch_queue_t queue2 = dispatch_get_main_queue();

    dispatch_async(queue2, ^{

        dispatch_sync(queue2,^{

            NSLog(@"hello");

        });

    });

    //c.Serial Dispatch Queue 也会引起相同的问题

    dispatch_queue_t queue3 = dispatch_queue_create("Dispatch Queue的名称", NULL);

    dispatch_async(queue3, ^{

        dispatch_sync(queue3, ^{

            NSLog(@"hello");

        });

    });


dispatch_apply

//dispatch_apply函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待追加的全部处理执行结束。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_apply(10, queue, ^(size_t index) {

        NSLog(@"%zu",index);

    });

    NSLog(@"done");

    //参数1:重复次数

    //参数2:指定要追加处理的Dispatch Queue

    //参数3:指定记述要执行处理的Block

    //注,参数3为带有参数的block,这是为了按第一个参数重复追加Block并区分各个Block而使用

    //建议:由于dispatch_apply函数也与dispatch_sync函数相同,会等待处理执行结束,因此推荐在dispatch_async函数中非同步地执行dispatch_apply函数。

    dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(queue2, ^{

        dispatch_apply(10, queue2, ^(size_t index) {

            NSLog(@"%zu",index);

        });

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"done");

        });

    });


dispatch_suspend和dispatch_resume

dispatch_queue_t queue = dispatch_queue_create("Dispatch Queue的名称", NULL);

    //dispatch_suspend函数挂起指定的Dispatch Queue。

    dispatch_suspend(queue);

    //挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行。

    //dispatch_resume函数恢复指定的Dispatch Queue。

    dispatch_resume(queue);

    //恢复则使得这些处理(追加到Dispatch Queue中但尚未执行的处理)能够继续执行。

    //注,这些函数对已经执行的处理没有影响。


Dispatch Semaphore

//Dispatch Semaphore是持有计数的信号。计数为0时等待,计数为1或大于1时,减去1而不等待。

    //通过dispatch_semaphore_create函数生成Dispatch Semaphore。注意,生成的Dispatch Semaphore在使用结束后必须由程序员进行释放(现在GCD对象已经纳入了ARC的管理范围,在纳入之前是需要程序员按照内存管理的思考方式来手动管理GCD对象的),同Dispatch Queue。

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

    //参数1:表示计数的初始值。本例将计数值初始化为"1"。

    //通过dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值大于或等于1。当计数值大于等于1,或者在待机中计数值大于等于1时,对该计数进行减法并从dispatch_semaphore_wait函数返回。

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    //参数1:等待的Dispatch Semaphore

    //参数2:与dispatch_group_wait函数等相同,指最多等待的时间(超时)。

    //返回:若计数值大于等于1,返回值为0。否则,返回值不为0。

    //通过返回值进行分支处理

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);

    long result = dispatch_semaphore_wait(semaphore, time);

    if (result == 0) {

        /*

        *  Dispatch Semaphore 的计数值达到大于等于1

        *  或者在待机中的指定时间内Dispatch Semaphore的计数值达到大于等于1

        *  Dispatch Semaphore 的计数值减去1

        */

    }else{

        /*

        *  Dispatch Semaphore 的计数值为0

        */

    }

    //实际应用的例子

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //生成Dispatch Semaphore,Dispatch Semaphore的计数初始值设定为"1",保证可访问NSMutableArray类对象的线程同时只能有一个。

    dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(1);

    NSMutableArray *array = [[NSMutableArray alloc]init];

    for (int i = 0; i<10000; i++) {

        dispatch_async(queue, ^{

            //等待Dispatch Semaphore的计数值达到大于等于1时,将Dispatch Semaphore的计数值减去1

            dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);

            //执行到此时的Dispatch Semaphore的计数值恒为"0",即其它线程处于等待状态,所以可访问NSMutableArray类对象的线程只有1个,因此可安全的进行更新

            [array addObject:[NSNumber numberWithInt:i]];

            //通过dispatch_semaphore_signal函数将Dispatch Semaphore的计数值加1

            dispatch_semaphore_signal(semaphore2);

            //之后,如果有其它通过dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值大于等于1的线程,就由最先等待的线程执行

        });

    }


dispatch_once

//dispatch_once函数是保证在应用程序中只执行一次指定处理的API,比如初始化。

    //使用是否初始化的标志变量来判断是否初始化

    static  int initialized = NO;

    if (initialized == NO) {

        //初始化处理

        initialized = YES;

    }

    //在大多数情况这种方式下是安全的。但是在多核CPU中,在正在更新表示是否初始化的标志变量时读取,就有可能多次执行初始化处理

    //使用dispatch_once函数

    static dispatch_once_t pred;

    dispatch_once (&pred,^{

        //初始化处理

    });

    //dispatch_once函数即使在多线程环境下执行,也可保证百分比安全。


GCDAsyncSocket快速开发Socket通信

参考:http://blog.csdn.net/bianhuanshizhe/article/details/69426022


如有转载,请说明原处

相关文章

网友评论

      本文标题:iOS多线程之GCD

      本文链接:https://www.haomeiwen.com/subject/qyeshftx.html