iOS-GCD的学习记录(1)

来源:互联网 发布:轮显源码 编辑:程序博客网 时间:2024/05/19 10:40

GCD的学习记录,总结给自己,参考了很好地博客,然后代码上加了一点点自己的东西

Dispatch Queue,如名所示,它是一个队列,用于存储要执行的任务。程序员可以用block语法编写要执行的任务,再通过dispatch_async函数将它加入到一个dispatch队列中。然后dispatch队列会按照FIFO的顺序执行这些任务,如图7-1所示。

GCD中有2种dispatch队列。一种是串行队列,串行队列中前一个任务执行完毕后,后一个任务才开始执行。另外一种是并行队列,并行队列可以同时执行多个任务。如图7-2所示。


1. 创建Serial Dispatch Queue

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

当创建多个Serial Dispatch Queue时,各个Serial Dispatch Queue将并行执行。但每个Serial Dispatch Queue中同时只能执行一个任务。


理论上使用dispatch_queue_create方法可以创建任意多个Serial Dispatch Queue,但如果创建的串行队列过多,而每一个队列都会生成一个新线程,从而就会导致过多的使用多线程,消耗大量内存,引起大量的上下文切换,这样会大幅度降低系统的响应性能。所以决不能随意的大量生产Serial Dispatch Queue。

2.创建Concurrent Dispatch Queue 并行队列

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

dispatch_queue_t concurrentQueue = dispatch_queue_create(“ConcurrentQueue”,DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_create方法的返回值为表示Dispatch Queue的“dispatch_queue_t”类型。该方法的第一个参数用于指定Dispatch Queue的名称,推荐使用逆序的域名来表示。该名称将在Xcode和Instruments的调试器中出现,便于调试。第二个参数如果设置为NULL,则生成Serial Dispatch Queue;设置为DISPATCH_QUEUE_CONCURRENT则生成Concurrent Dispatch Queue。

dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL);        dispatch_async(serialQueue, ^{        sleep(5);        NSLog(@"串行线程1");            });        dispatch_async(serialQueue, ^{        sleep(5);        NSLog(@"串行线程2");            });        dispatch_queue_t global = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_CONCURRENT);        dispatch_async(global, ^{                NSLog(@"并行线程1");            });        dispatch_async(global, ^{                NSLog(@"并行线程2");            });


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [self goDoSomethingLongAndInvolved];        dispatch_async(dispatch_get_main_queue(), ^{            [textField setStringValue:@"Done doing something long and involved"];        });});


3.Main&Global Dispatch Queue

Main Dispatch Queue是在主线程中执行任务的Dispatch Queue。因为主线程只有1个,所以Main Dispatch Queue是Serial Dispatch Queue。追加到Main Dispatch Queue中的任务将在主线程的RunLoop中执行。因为是在主线程中执行,所以应该只将用户界面更新等一些必须在主线程中执行的任务追加到Main Dispatch Queue中。以前的NSObject类的performSelectorOnMainThread实例方法也是这样。

Global Dispatch Queue是所有应用程序都能使用的Concurrent Dispatch Queue。大多数情况下,可以不必通过dispatch_queue_create函数生成Concurrent Dispatch Queue,而是只要获取Global Dispatch Queue使用即可。

Global Dispatch Queue有4个优先级,分别是:High、Default、Low、Background。XNU内核负责管理用于Global Dispatch Queue的线程,并将Global Dispatch Queue的优先级作为其线程的执行优先级来使用。所以,在向Global Dispatch Queue中追加任务时,应该选择合适的优先级。不过,XNU内核并不能保证线程的实时性,这里的优先级只是一个大致的判断。


各个Dispatch Queue的获取方法如下:

dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();  dispatch_queue_t globalDispatchQueueHigh =    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);  dispatch_queue_t globalDispatchQueueDefault =    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t globalDispatchQueueLow =    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);  dispatch_queue_t globalDispatchQueueBackground =    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);


4.dispatch_set_target_queue

dispatch_set_target_queue函数用于设置一个”目标”队列。这个函数主要用来为新创建的队列设置优先级。当用dispatch_queue_create函数创建一个队列后,无论创建的是并行队列还是串行队列,队列的优先级都和全局dispatch队列的默认优先级一样。创建队列后,你可以用这个函数来修改队列的优先级。下面的代码演示了如何给一个串行队列设置background优先级。


dispatch_queue_t mySerialDispatchQueue =dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);dispatch_queue_t globalDispatchQueueBackground =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);

在上面代码中,dispatch_set_target_queue函数的第一个参数是要设置优先级的队列,第二个参数是一个全局的dispatch队列,它会被作为目标队列。这段代码会使队列拥有和目标队列一样的优先级(稍后会解释这里的机制)。如果你将主线程队列或者全局队列传递给dispatch_set_target_queue函数的第一参数,结果不确定,最后不要这么干。使用dispatch_set_target_queue函数不仅能够设置优先级,也能创建队列的层次体系。如图7-8所示,一个串行队列被设置成了多个串行队列的目标队列,在目标队列上,一次只会有一个队列被执行。


5.dispatch_after

dispatch_after用于在队列中定时执行任务。当你想在一段时间后执行一个任务,那么就可以用这个函数。例如,下面代码中,3秒过后,指定的block会被添加到主线程队列上。


dispatch_queue_t global = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_CONCURRENT);        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC);    dispatch_after(time, global, ^{        NSLog(@"waited at least three seconds.");    });    dispatch_after(time, global, ^{        NSLog(@"waited at least three seconds.");    });

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);dispatch_after(time, dispatch_get_main_queue(), ^{NSLog(@"waited at least three seconds.");});

代码中,”ull”代表”unsigned long long”类型。请注意,dispatch_after函数并不会在指定的时间后立即执行任务,时间到后,它只将任务加入到队列上。因此这段代码的作用和你在3秒钟后用dispatch_async函数将block加到主线程上面的作用是一样的。主线程队列是在RunLoop上执行的,因此,假如RunLoop每1/60秒执行一次任务,那么上面添加的block会在3秒~3+1/60秒后被执行。如果主线程队列上加了很多任务,或者主线程延迟了,时间可能更晚。所以,将dispatch_after作为一个精确的定时器使用是有问题的。如果你只是想粗略的延迟一下任务,这个函数还是很有用的。

函数的第二个参数指定了一个dispatch队列,用于添加任务。第三个参数,是一个block,即要执行的任务。第一参数指定了延迟的时间,是一个dispatch_time_t类型的参数。这个值可以用dispatch_time函数或dispatch_walltime函数来创建。

dispatch_time的第一个参数是指定的起始时间,第二个参数是以纳秒为单位的一个时间间隔。这个函数以起始时间和时间间隔来创建一个新的时间。如例中所示,通常以DISPATCH_TIME_NOW来作为第一个参数的值,它代表了当前的时间。在下面这段代码中,你可以得到一个dispatch_time_t类型的表示1秒钟之后的时间的变量。


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

第二个参数中,NSEC_PER_SEC和数字的乘积会得到一个以纳秒为单位的时间间隔值。如果使用NSEC_PER_MSEC,就会得到一个以毫米为单位的时间间隔值。下面的代码,演示了如何获得一个150毫米之后的时间。


dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 150ull * NSEC_PER_MSEC);

dispatch_walltime函数以一个timespec结构体类型(来自POSIX接口)的时间来创建一个dispatch_time_t类型的时间。dispatch_time主要用于创建一个相对时间。dispatch_walltime函数则是用于创建一个绝对时间。比如,你可以用dispatch_walltime函数为dispatch_after函数创建一个诸如”2011年11月11日 11:11:11”这样的绝对时间,用来做一个闹钟,但是它是低精度的。通过NSDate类对象,可以很方便的创建一个timespec结构的时间,如下面代码所示。


dispatch_time_t getDispatchTimeByDate(NSDate *date){        NSTimeInterval interval;        double second, subsecond;        struct timespec time;        dispatch_time_t milestone;        interval = [date timeIntervalSince1970] + 3;        subsecond = modf(interval, &second);        time.tv_sec = second;        time.tv_nsec = subsecond * NSEC_PER_SEC;        milestone = dispatch_walltime(&time, 0);        return milestone;    }NSLog(@"222");        dispatch_time_t aTime = getDispatchTimeByDate([NSDate date]);        dispatch_after(aTime, dispatch_get_main_queue(), ^(void){                //aTime 之后在主线程干点什么                //dispatch_get_main_queue() 随便改成其他线程也可以        NSLog(@"111");    });



参考文章:筒子楼 coding
0 0
原创粉丝点击