IOS GCD的使用详解

来源:互联网 发布:知乎创始人 非诚勿扰 编辑:程序博客网 时间:2024/05/02 04:45

IOS GCD的使用详解

GCD介绍:

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

GCD是一个替代诸如NSThread等技术的很高效和强大的技术。GCD完全可以处理诸如数据锁定和资源泄漏等复杂的异步编程问题.


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 QueueOS XiOSXNU内核会基于Dispatch Queue中的任务数量、CPU核数和CPU负荷等当前系统状态来决定创建多少个线程和并行执行多少个任务。

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

简言之:Serial Dispatch Queue串行代码;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_releasedispatch_retain来手动管理(引用计数式)。

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


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


声明一个队列

如下会返回一个用户创建的队列:dispatch_queue_t myQueue = dispatch_queue_create("com.iphonedevblog.post", NULL);其中,第一个参数是标识队列的,第二个参数是用来定义队列的参数(目前不支持,因此传入NULL)。 

执行一个队列 

如下会异步执行传入的代码: 

 dispatch_async(myQueue, ^{ [self doSomething]; });其中,首先传入之前创建的队列,然后提供由队列运行的代码块。

声明并执行一个队列

如果不需要保留要运行的队列的引用,可以通过如下代码实现之前的功能: dispatch_async(dispatch_queue_create ("com.iphonedevblog.post", NULL), ^{ [self doSomething]; });如果需要暂停一个队列,可以调用如下代码。暂停一个队列会阻止和该队列相关的所有代码运行。dispatch_suspend(myQueue);暂停一个队列  

如果暂停一个队列不要忘记恢复。暂停和恢复的操作和内存管理中的retainrelease类似。调用dispatch_suspend会增加暂停计数,而dispatch_resume则会减少。队列只有在暂停计数变成零的情况下才开始运行。dispatch_resume(myQueue);恢复一个队列 从队列中在主线程运行代码些操作无法在异步队列运行,因此必须在主线程(每个应用都有一个)上运行。UI绘图以及任何对NSNotificationCenter的调用必须在主线程长进行。在另一个队列中访问主线程并运行代码的示例如下:dispatch_sync(dispatch_get_main_queue(), ^{ [self dismissLoginWindow]; });注意,dispatch_suspend(以及dispatch_resume)在主线程上不起作用。

使用GCD,可以让你的程序不会失去响应.多线程不容易使用,用了GCD,会让它变得简单。你无需专门进行线程管理.


让你的程序保持响应的原则:

1.不要柱塞主线程

2.把工作一到其他线程中做。

3.做完后更新主线程的UI.


从网络抓取数据,然后更新UI,我们经常这么做:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // long-running task

            dispatch_async(dispatch_get_main_queue(), ^{

                    // update UI

            });

    });

dispatch_async() 调用以后立即返回,dispatch_sync() 调用以后等到block执行完以后才返回,dispatch_sync()会阻塞当前线程。 


dispatch_async接口是用来提交block给指定queue进行异步执行,如果成功提交,那么立刻返回,程序继续往下执行。注意,block是定义在stack上的,需要复制到pile上,再执行。与之对应的是dispatch_sync接口,提交block提供同步执行。这个接口会等到block执行结束以后才返回,在主线程这样做是没意义的,主要在子线程上使用。

有一种写法会有死锁

dispatch_queue_t exampleQueue = dispatch_queue_create("xxx.identifier", NULL);

    dispatch_sync( exampleQueue,^{

            printf("I am now deadlocked...\n");

    });});


3.普通控制

dispatch_once:这个接口可以保证在整个应用程序生命周期中某段代码只被执行一次

dispatch_after:让线程等个两秒钟然后给个提示

    double delayInSeconds = 2.0;

    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Hi~" message:@"message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];

        [alert show];

    });


dispatch_set_target_queue:可以设置一个dispatch queue的优先级,或者指定一个dispatch source相应的事件处理提交到那个queue上。注意,只是开始的顺序,如果前面没有按顺序来,这里dispatch_set_target_queue(serialQ,mainQ),让serialQ与主线程的级别一样高。第二个参数是参照物,让第一个线程和参照线程的优先级一样。

dispatch_apply:执行某段代码若干次。这样做的另一个好处是,处理NSArray中元素的Block并行执行,速度更快

Crayon Syntax Highlighter v2.7.1

    dispatch_queue_t queue =

      dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_apply([array count], queue, ^(size_t index) {

      NSLog(@"%zu: %@", index, [array objectAtIndex:index]);

});

dispatch group:该机制允许我们监听一组任务是否完成

    void(^blk0)(void) = ^(void) {

        NSLog(@"blk0 is running...");

    };

    void(^blk1)(void) = ^(void) {

        NSLog(@"blk1 is running...");

    };

    void(^blk2)(void) = ^(void) {

        NSLog(@"blk2 is running...");

    };

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group, serialQ, blk0);

    dispatch_group_async(group, serialQ, blk1);

    dispatch_group_async(group, serialQ, blk2);

    dispatch_group_notify(group, mainQ, ^{

        NSLog(@"finished...");

    });


或者说同步地等待一段时间看是否结束:

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);//从执行到这一行开始等一秒钟,看是否完成,没有完成输出49,完成输出为0

    NSLog(@"%ld",dispatch_group_wait(group, time));


dispatch_barrier_async:提交的任务会等它前面的任务执行结束才开始,然后它后面的任务必须等它执行完毕才能开始。

    dispatch_queue_t queue = dispatch_queue_create("xxx.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{

        [NSThread sleepForTimeInterval:2];

        NSLog(@"dispatch_async1");

    });

    dispatch_async(queue, ^{

        [NSThread sleepForTimeInterval:4];

        NSLog(@"dispatch_async2");

    });

    dispatch_barrier_async(queue, ^{

        NSLog(@"dispatch_barrier_async");

        [NSThread sleepForTimeInterval:4];

    });

    dispatch_async(queue, ^{

        [NSThread sleepForTimeInterval:1];

        NSLog(@"dispatch_async3");  

    });







0 0
原创粉丝点击