iOS开发日志之GCD常用API

来源:互联网 发布:linux 编译安装php 编辑:程序博客网 时间:2024/06/06 19:17

简介:

   GCD(Grand Central Dispatch)是从OS X Snow Leopard和iOS 4开始引入的新一代的多线程编程技术。开发者只需定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并有计划的执行任务。并且由于线程管理是作为系统的一部分来实现的,因此系统可以统一管理,它会决定什么时候创建新线程,创建哪些新线程,多个任务是顺序执行还是并发执行。这样就比以前的线程编程更有效率。


dispatch_async(queue, ^{  // dispatch_async是异步方法。长时间处理,例如数据库访问  dispatch_async(dispatch_get_main_queue(), ^{    // 到主线程队列中执行    // 例如界面更新  });});

Dispatch Queue(调度队列)

使用GCD进行编程,开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。其中,“定义想执行的任务”使用Block语法来完成。Dispatch Queue中的任务按照FIFO的顺序进行处理,也就是先进入的任务先处理。另外,Dispatch Queue分为串行和并发两种。

Serial Dispatch Queue要求等待正在执行的任务完成,再执行下一个。而Concurrent Dispatch Queue中后面的任务可以不必等待正在执行的任务执行完成就可以开始执行,也就是同时可以执行多个任务。换句话说,其实就是Serial Dispatch Queue只会创建一个线程来执行任务,而Concurrent Dispatch Queue会创建多个线程同时执行多个任务。

对于Concurrent Dispatch Queue,OS X和iOS的XNU内核会基于Dispatch Queue中的任务数量、CPU核数和CPU负荷等当前系统状态来决定创建多少个线程和并行执行多少个任务。

一般情况下:如果要求在不能改变执行顺序或不想并行执行多个任务时使用Serial Dispatch Queue。

简言之:Serial Dispatch Queue串行代码;Concurrent Dispatch Queue并行代码。




(一)dispatch_queue_create创建dispatch_queue

dispatch_queue_create 用于创建用户线程队列。可以创建Serial/Concurrent Dispatch Queue 两种队列,即串行与并行队列。

1. 创建Serial Dispatch Queue。

dispatch_queue_t serialQueue = dispatch_queue_create(“com.SerialQueue”, NULL);

可以创建多个串行队列,串行队列也可以并行执行。决不能随意的大量生产Serial Dispatch Queue。

2. 创建Concurrent Dispatch Queue


dispatch_queue_t concurrentQueue = dispatch_queue_create(“com.ConcurrentQueue”,    DISPATCH_QUEUE_CONCURRENT);

Concurrent Dispatch Queue不过创建多少都没有问题,因为Concurrent Dispatch Queue所使用的线程由系统的XNU内核高效管理,不会影响系统性能。

3. 内存管理 由dispatch_queue_create方法生成的Dispatch Queue并不能由ARC来自动管理内存。可以使用dispatch_release、dispatch_retain来手动管理(引用计数式)。

但在目前看来,所用的OSX-10.8 开启的ARC已经不需要再用dispatch_release()来做管理。

 4:对于串行队列,每创建一个串行队列,系统就会对应创建一个线程,同时这些线程都是并行执行的,只是在串行队列中的任务是串行执行的。大量的创建串行队列会导致大量消耗内存,这是不可取的做法。串行队列的优势在于他是一个线程,所以在操作一个全局数据时候是线程安全的。当想并行执行而不发生数据竞争时候可以用并行队列操作




(二)main&global dispatch_queue

Main Dispatch Queue是在主线程中执行任务的Dispatch Queue。因为主线程只有1个,所以Main Dispatch Queue是Serial Dispatch Queue。追加到Main Dispatch Queue中的任务将在主线程的RunLoop中执行。因为是在主线程中执行,所以应该只将用户界面更新等一些必须在主线程中执行的任务追加到Main Dispatch Queue中。


dispatch_queue_t dispatch_main_queue = dispatch_get_main_queue();

 

Global Dispatch Queue是所有应用程序都能使用的Concurrent Dispatch Queue。大多数情况下,可以不必通过dispatch_queue_create函数生成Concurrent Dispatch Queue,而是只要获取Global Dispatch Queue使用即可。Global Dispatch Queue有4个优先级,分别是:High、Default、Low、Background。


dispatch_queue_t dispatch_global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 



(三)dispatch_group

如果想在dispatch_queue中所有的任务执行完成后在做某种操作,在串行队列中,可以把该操作放到最后一个任务执行完成后继续,但是在并行队列中怎么做呢。这就有dispatch_group 成组操作。比如


    dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT);    dispatch_group_t dispatchGroup = dispatch_group_create();    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){        NSLog(@"dispatch-1");    });    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){        NSLog(@"dspatch-2");    });    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){        NSLog(@"end");    });

上面的 log1 和log2输出顺序不定,因为是在并行队列上执行,当并行队列全部执行完成后,最后到main队列上执行一个操作,保证“end”是最后输出。 另外,这里也可以不用创建自己的并行队列,用全局的global,那个也是个并行队列. dispatch_get_gloable_queue(0,0);



(四)dispatch_barrier_async

先看段代码


    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-1");    });    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-2");    });    dispatch_barrier_async(concurrentQueue, ^(){        NSLog(@"dispatch-barrier");     });    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-3");    });    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-4");    });
dispatch_barrier_async 作用是在并行队列中,等待前面两个操作并行操作完成,这里是并行输出
dispatch-1,dispatch-2
然后执行
dispatch_barrier_async中的操作,(现在就只会执行这一个操作)执行完成后,即输出
"dispatch-barrier,最后该并行队列恢复原有执行状态,继续并行执行
dispatch-3,dispatch-4



(五)dispatch_async和dispatch_sync

dispatch_sync(),同步添加操作。他是等待添加进队列里面的操作完成之后再继续执行。


    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);    NSLog(@"1");    dispatch_sync(concurrentQueue, ^(){        NSLog(@"2");        [NSThread sleepForTimeInterval:10];        NSLog(@"3");    });    NSLog(@"4"); 输出 :        

11:36:25.313 GCDSeTest[544:303] 1

11:36:25.313 GCDSeTest[544:303] 2

11:36:30.313 GCDSeTest[544:303] 3//模拟长时间操作

11:36:30.314 GCDSeTest[544:303] 4


dispatch_async ,异步添加进任务队列,它不会做任何等待


    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);    NSLog(@"1");    dispatch_async(concurrentQueue, ^(){        NSLog(@"2");        [NSThread sleepForTimeInterval:5];        NSLog(@"3");    });    NSLog(@"4"); 输出:

 11:42:43.820 GCDSeTest[568:303] 1

 11:42:43.820 GCDSeTest[568:303] 4

 11:42:43.820 GCDSeTest[568:1003] 2

 11:42:48.821 GCDSeTest[568:1003] 3//模拟长时间操作时间




(六)dispatch_apply

dispathc_apply 是dispatch_sync 和dispatch_group的关联API.它以指定的次数将指定的Block加入到指定的队列中。并等待队列中操作全部完成.


    NSArray *array = [NSArray arrayWithObjects:@"/Users/chentao/Desktop/copy_res/gelato.ds",                      @"/Users/chentao/Desktop/copy_res/jason.ds",                      @"/Users/chentao/Desktop/copy_res/jikejunyi.ds",                      @"/Users/chentao/Desktop/copy_res/molly.ds",                      @"/Users/chentao/Desktop/copy_res/zhangdachuan.ds",                      nil];    NSString *copyDes = @"/Users/chentao/Desktop/copy_des";    NSFileManager *fileManager = [NSFileManager defaultManager];    dispatch_async(dispatch_get_global_queue(0, 0), ^(){        dispatch_apply([array count], dispatch_get_global_queue(0, 0), ^(size_t index){            NSLog(@"copy-%ld", index);            NSString *sourcePath = [array objectAtIndex:index];            NSString *desPath = [NSString stringWithFormat:@"%@/%@", copyDes, [sourcePath lastPathComponent]];            [fileManager copyItemAtPath:sourcePath toPath:desPath error:nil];        });        NSLog(@"done");    });

输出 copy-index 顺序不确定,因为它是并行执行的(dispatch_get_global_queue是并行队列),但是done是在以上拷贝操作完成后才会执行,因此,它一般都是放在dispatch_async里面(异步)。实际上,这里 dispatch_apply如果换成串行队列上,则会依次输出index,但这样违背了我们想并行提高执行效率的初衷。




(七)dispatch_semaphore

dispatch_semaphore 信号量基于计数器的一种多线程同步机制。在多个线程访问共有资源时候,会因为多线程的特性而引发数据出错的问题。


    dispatch_queue_t queue =dispatch_get_global_queue(0, 0);

    dispatch_semaphore_t semaphore =dispatch_semaphore_create(1);

    NSMutableArray *array = [NSMutableArrayarray];

    for (int index =0; index < 100000; index++) {

        dispatch_async(queue, ^(){

            dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);//

            NSLog(@"addd :%d", index);

            [array addObject:[NSNumbernumberWithInt:index]];

            dispatch_semaphore_signal(semaphore);

        });

    }


dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 如果semaphore计数大于等于1.计数-1,返回,程序继续运行。如果计数为0,则等待。这里设置的等待时间是一直等待。dispatch_semaphore_signal(semaphore);计数+1.在这两句代码中间的执行代码,每次只会允许一个线程进入,这样就有效的保证了在多线程环境下,只能有一个线程进入。


0 0
原创粉丝点击