Concurrency4

来源:互联网 发布:mysql 错误日志路径 编辑:程序博客网 时间:2024/04/27 22:11

一、GCD中的延时操作

1.关于延迟操作的说明
我们在程序中经常用到延迟一段时间后执行某段代码,通常情况下我们利用的是NSObject中的performSelector:withObject:afterDelay方法。
eg:

 - (void)printString:(NSString *)paramString{    NSLog(@"%@",paramString);} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    [self performSelector:@selector(printString:) withObject:@"GCD Central Dispatch" afterDelay:3.0];}

2.GCD中的延迟操作实现
在GCD中也有延迟操作,只不过GCD中的延迟操作有两种实现方法,其中一种的对象是block另一个对象是C函数。
它们分别是:dispatch_after函数、dispatch_after_f函数。

dispatch_after 它有3个参数:
- Delay in nanoseconds : 延迟的时间,单位是纳秒。
- Dispatch queue : block对象所在的队列。
- Block object : block对象。该block对象既不能有参数,也不能有返回值。

dispatch_after_f它有4个参数:
- Delay in nanoseconds:延迟时间,单位是纳秒。
- Dispatch queue : c函数所在的队列。
- Context : C函数所在的堆内存地址。
- C function : C函数的地址。

eg:下面分别是这两种方式的用法
1) dispatch_after

- (void)blockObjectProcess{    double delayInSeconds = 2.0;    dispatch_time_t      = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);    dispatch_queue_t currentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_after(delyInNanoSeconds, currentQueue, ^{        NSLog(@"block Object");    });}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    [self blockObjectProcess];}

2) dispatch_after_f

- (void)cFunctionProcess{    double delayInSeconds = 2.0;    dispatch_time_t delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);    dispatch_queue_t currentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_after_f(delayInNanoSeconds, currentQueue, NULL, processSomething);}

二、GCD中代码只执行一次的操作

1.说明
在代码中我们经常会用到让某一段代码只执行一次的逻辑,比如单例。在GCD中提供了 dispatch_once的函数提供了这种功能。
2.GCD中执行一次操作的实现
dispatch_once 有两个参数:
- Token : A token of type dispatch_once_t that holds the token generated by GCD when the block of code is executed for the first time. If you want a piece of code to be executed at most once, you must specify the same token to this method whenever it is invoked in the app. We will see an example of this soon.
- Block object : block代码块,该代码块既没有参数也没有返回值。

eg:

static dispatch_once_t onceToken;void (^executedOnlyOnce)(void) = ^{    static NSUInteger numberOfEntries = 0;    numberOfEntries++;    NSLog(@"Executed %lu time(s)",(unsigned long)numberOfEntries);};- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_once(&onceToken, ^{        dispatch_async(concurrentQueue, executedOnlyOnce);    });    dispatch_once(&onceToken, ^{        dispatch_async(concurrentQueue, executedOnlyOnce);    });}

三、分组任务和GCD

1.说明
分组任务的场景也就是当你想让一组的block代码在GCD中一个接一个的执行时,就好比一个任务依赖于另一个任务的时候。GCD提供了dispatch_group_create函数来创建组。
GCD给我们提供了创建组的方法,这就能让我们把一组任务都放到一个地方,当执行完成的时候,就会收到一个通知。
比如,你有下面的这些方法–

 - (void)reloadTableView{    // reload the tableView here    NSLog(@"%s",__FUNCTION__);} - (void) reloadScrollView {    // Do the work here    NSLog(@"%s",__FUNCTION__);} - (void) reloadImageView{    // Reload Image view here    NSLog(@"%s",__FUNCTION__);}

此时这些方法都是空的。现在我们想一个接一个的调用这三个方法,并且我们也想知道GCD什么时候完成了这三个方法的调用,我们期望在完成调用的时候能向用户发送一个消息。基于这种情景,我们应该使用一个组,你应该知道以下3个函数当你使用GCD中的组的时候:
- dispatch_group_create :创建一个组
- dispatch_group_async :向一个组中提交block代码。
- dispatch_group_notify : 允许你提交一个block对象当组中所有的任务都执行完成的时候。
eg:下面是代码示例

- (void)groupGCDTest{    dispatch_group_t taskGroup = dispatch_group_create();    dispatch_queue_t mainQueue = dispatch_get_main_queue();    /* Reload the table view on the main queue */    dispatch_group_async(taskGroup, mainQueue, ^{        [self reloadTableView];    });    /* Reload the scroll view on the main queue */    dispatch_group_async(taskGroup, mainQueue, ^{        [self reloadScrollView];    });    /* Reload the image view on the main queue */    dispatch_group_async(taskGroup, mainQueue, ^{        [self reloadImageView];    });    /* At the end of we are done,dispatch the following block */    dispatch_group_notify(taskGroup, mainQueue, ^{       [[[UIAlertView alloc] initWithTitle:@"Finish"                                  message:@"All task are finished"                                 delegate:nil                        cancelButtonTitle:@"OK"                         otherButtonTitles:nil, nil] show];    });}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    [self groupGCDTest];}

作为对dispatch_group_async的补充,我们也可以利用 dispatch_group_async_f 函数来提交一个C函数往一个组中。
eg:提交C函数的代码示例–

void reloadAllComponents(void *context){    ViewController *self = (__bridge ViewController *)context;    [self reloadTableView];    [self reloadScrollView];    [self reloadImageView];}- (void)groupGCDTest{    dispatch_group_t taskGroup = dispatch_group_create();    dispatch_queue_t mainQueue = dispatch_get_main_queue();    dispatch_group_async_f(taskGroup, mainQueue, (__bridge_retained void *)self, reloadAllComponents);    /* At the end of we are done,dispatch the following block */    dispatch_group_notify(taskGroup, mainQueue, ^{       [[[UIAlertView alloc] initWithTitle:@"Finish"                                  message:@"All task are finished"                                 delegate:nil                        cancelButtonTitle:@"OK"                         otherButtonTitles:nil, nil] show];    });}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    [self groupGCDTest];}

四、利用GCD创建自己的操作队列

1.说明
我们可以创建自己命名的队列。GCD给我们提供了dispatch_queue_create 函数。

利用GCD,你自己去创建串行队列。串行队列中的任务是按照FIFO的顺序执行的。在串行队列中的异步任务不会在主线程上运行,这就使串行队列的FIFO是非常可取的。

所有被提交到串行队列中的同步任务都会在当前正在运行的线程上执行,而所有被提交到串行队列上的异步任务都会在非主线程上运行。

我们可以利用dispatch_queue_create 函数来创建一个串行队列。这个函数的第一个参数是一个C语言的字符串string(char*),它是系统中的队列的一个独一无二的identifer标识。我需要强调一下,这里的identifer的级别是系统级别,也就是说在同一个手机上如果你的app中创建一个名字为serialQueue1的串行队列而其他人的app中也创建了一个相同名字的队列,那么这就会引起相同名称的串行队列无法创建的情况。正是由于这个原因,Apple强烈建议你使用标识符反向DNS格式。反向标识符通常通过这种方式构造:com.COMPANY.PRODUCT.IDENTIFER. 举个例子,我创建两个串行队列并把它们的名字设置为如下:
com.pixolity.GCD.serialQueue1
com.pixolity.GCD.serialQueue2

eg:下面看一个代码示例

- (void)createMySerialQueue{    dispatch_queue_t firstSerialQueue = dispatch_queue_create("com.pixolity.GCD.serialQueue1", 0);    dispatch_async(firstSerialQueue, ^{        NSUInteger counter = 0;        for (counter = 0; counter < 5; counter++) {            NSLog(@"First iteration,counter = %lu",(unsigned long)counter);        }    });    dispatch_async(firstSerialQueue, ^{        NSUInteger counter = 0;        for (counter = 0; counter < 5; counter++) {            NSLog(@"Second iteration,counter = %lu",(unsigned long)counter);        }    });    dispatch_async(firstSerialQueue, ^{        NSUInteger counter = 0;        for (counter = 0; counter < 5; counter++) {            NSLog(@"Third iteration,counter = %lu",(unsigned long)counter);        }    });}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    [self createMySerialQueue];}

运行结果如图:
这里写图片描述

很明显,尽管我们为block对象分配的是在串行队列中的异步任务,这个队列执行它内部的代码是按照FIFO的顺序。
我们同样也可以利用dispatch_async_f函数来替代 dispatch_async ,去执行C函数。
eg:下面是代码示例

void firstIteration(void *paramContext){    NSUInteger counter = 0;    for (counter = 0; counter < 5; counter++) {        NSLog(@"First iteration,counter = %lu",(unsigned long)counter);    }}void secondIteration(void *paramContext){    NSUInteger counter = 0;    for (counter = 0; counter < 5; counter++) {        NSLog(@"Second iteration,counter = %lu",(unsigned long)counter);    }}void thirdIteration(void *paramContext){    NSUInteger counter = 0;    for (counter = 0; counter < 5; counter++) {        NSLog(@"Third iteration,counter = %lu",(unsigned long)counter);    }}- (void)createMySerialQueue{    dispatch_queue_t firstSerialQueue = dispatch_queue_create("com.pixolity.GCD.serialQueue1", 0);    dispatch_async_f(firstSerialQueue, NULL, firstIteration);    dispatch_async_f(firstSerialQueue, NULL, secondIteration);    dispatch_async_f(firstSerialQueue, NULL, thirdIteration);}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    [self createMySerialQueue];}
0 0