Grand Central Dispatch(GCD)初探

来源:互联网 发布:js除以10000四舍五入 编辑:程序博客网 时间:2024/05/16 09:19

GCD提供和管理一些先进先出队列,你的应用程序可以以块的形式向这些队列提交任务。提交给队列的块在一个完全由系统维护的线程池上执行。线程对于运行在其上的任务并不作出任何保证。(No guarantee is made as to the thread on which a task executes.

 GCD 提供了三种队列:

  • 主队列 Main: tasks execute serially on your application’s main thread// 任务按照次序在应用的主线程上执行

  • 同步队列 Concurrent: tasks are dequeued in FIFO order, but run concurrently and can finish in any order.// 按先进先出次序出列,同步运行,但完成的次序是随即的。

  • 串行队列 Serial: tasks execute one at a time in FIFO order// 按照先进先出的次序,每次执行一个任务

主队列是系统自动创建的,并且与应用主线程相关联。应用使用下面三个方法中的一个(仅一个)来引用被提交给主队列的块:
  • Calling dispatch_main

  • Calling UIApplicationMain (iOS) or NSApplicationMain (OS X)

  • Using a CFRunLoopRef on the main thread

使用同步队列来同步执行大量的任务。GCD自动创建了三个同步队列,对应用来说,这三个队列是全局的,而且仅通过它们的优先级来区别。应用通过 dispatch_get_global_queue 函数请求那些队列。因为那些同步队列对于应用来说是全局的,你不需要保持或者释放它们,即使你这样做,也会被忽略掉。在OS X v10.7以上的系统中,你也可以创建额外的同部队列。在你的代码模块中使用。

使用串行队列要保证任务按照一定的顺序执行。为每个串行队列确定一个特定的目的是一个好的习惯,例如保护一个资源或者同步关键过程。应用必须明确地创建和管理串行队列。你可以按需创建串行队列,但是应该避免使用他们替代同步队列,同时执行大量任务。

重要提示:GCD是C语言的API,不处理高层次语言的任何异常,应用应该在提交给队列的块返回之前,处理所有异常。

目前我主要用的是异步加载PDF文档的页面,我的思路是:后台每加载一页,则立即更新对应的UI。两个正确思路如下
1、使用的是dispatch_async、dispath_apply:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    // 异步操作    dispatch_async(dispatch_get_main_queue(), ^{        // UI更新    });});
dispatch_apply(num, globalQ, ^(size_t index) {    // 执行num次,index为当前次数,从0开始计数});
-(void)LoadPage:(NSInteger)nPage :(NSInteger)num{    if (mIsLoading == YES) {        return;// 正在加载中。。。。。。    }    mIsLoading = YES;    dispatch_queue_t displayQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_async(displayQueue, ^{        dispatch_apply(num, displayQueue, ^(size_t index){            // 执行num次            NSInteger curPage = nPage + index;            //NSLog(@"%zu",index);            NSLog(@"即将加载的页面为:%d",curPage);            // 加载数据            sleep(2);            // 更新页面            dispatch_async(dispatch_get_main_queue(), ^{                NSLog(@" 更新页面%d",curPage);            });// dispatch_get_main_queue                    });// dispatch_apply        mIsLoading = NO;// 加载结束    });// dispatch_async}
执行结果如下:

2、该思路使用一个串行队列来处理,具体如下:
if (mIsLoading == YES) {        return;// 正在加载中。。。。。。    }    mIsLoading = YES;    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);//创建串行队列    dispatch_async(serialQueue, ^{        NSLog(@"加载页面 %d!",1);        dispatch_async(dispatch_get_main_queue(), ^{            NSLog(@"更新UI 1!");        });// dispatch_get_main_queue            });// dispatch_async 3        dispatch_async(serialQueue, ^{        NSLog(@"加载页面 %d!",2);                dispatch_async(dispatch_get_main_queue(), ^{            NSLog(@"更新UI 2!");        });// dispatch_get_main_queue            });// dispatch_async 3        dispatch_async(serialQueue, ^{        NSLog(@"加载页面 %d!",3);                dispatch_async(dispatch_get_main_queue(), ^{            NSLog(@"更新UI 3!");        });// dispatch_get_main_queue            });// dispatch_async 3    mIsLoading = NO;// 加载结束
运行结果如下:
以前的一些错误思路:
1、
-(void)LoadPage:(NSInteger)nPage{     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        // 异步操作        NSLog(@"即将加载的页面为:%d",nPage);        // 加载数据        sleep(2);        dispatch_async(dispatch_get_main_queue(), ^{            // UI更新            NSLog(@" 更新页面%d",nPage);        });    });}
调用的时候,使用循环传入须加载的页码的值。看似正确,实际有很大的安全问题。本质问题就在于,循环是在前台执行,而加载却在后台。
2、
-(void)LoadPage:(NSInteger)nPage :(NSInteger)num   for (int i = nPage; i< nPage+num; i++) {        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{            // 异步操作            NSLog(@"即将加载的页面为:%d",i);            // 加载数据            sleep(2);            dispatch_async(dispatch_get_main_queue(), ^{                // UI更新                NSLog(@" 更新页面%d",i);            });        });    }}
这种方式跟上一种有异曲同工的错误,看看运行结果吧:

这明显是不正确的。