block是什么
block是一个C level的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似。用于回调函数的地方。两个对象间的通讯。实现轻量级的“代理”。

blocks和C语言函数指针的区别

如何调用blocks
调用block和C语言函数指针的调用一模一样

如何在 block 中修改外部变量?????
考虑到 block 的目的是为了支持并行编程,对于普通的 local 变量,我们就不能在 block 里面随意修改(原因很简单,block 可以被多个线程并行运行,会有问题的),而且如果你在 block 中修改普通的 local 变量,编译器也会报错。那么该如何修改外部变量呢?有两种办法,第一种是可以修改 static 全局变量;第二种是可以修改用新关键字 __block 修饰的变量。
__block关键字
一个Block的内部是可以引用自身作用域外的变量的,包括static变量,extern变量或自由变量(定义一个变量的时候,如果不加存储修饰符,默认情况下就是自由变量auto,auto变量保存在stack中的,除了auto之外还存在register,static等存储修饰符),
对于局部变量,在block中是只读的。在引入block的同时,还引入了一种特殊的关键字__block,用此声明一个局部变量可以被函数块修改。

实例:
void(^aBlock)(void) = 0;          // 声明一个block
    aBlock = ^(void){                 // 给block赋值
        NSLog(@"this is a block.");
    };
    aBlock();                            // 执行block


上面我们介绍了 block 及其基本用法,但还没有涉及并行编程。 block 与 Dispatch Queue 分发队列结合起来使用,是 iOS 中并行编程的利器。

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];

// 创建一个串行分发队列

dispatch_queue_t queue = dispatch_queue_create("studyBlocks", NULL);

// 将一个 block 任务加入到其中并行运行. 这样 block 就会在新的线程中运行,直到结束返回主线程

// 加入 dispatch_queue 中的 block 必须是无参数也无返回值的

dispatch_async(queue, ^(void){

int sum = 0;

for (int i = 0; i<100; i++) {

sum += i;

}

NSLog(@"sum:%d",sum);

});

dispatch_release(queue);

[pool drain];

1、dispatch_queue_t 类型 的定义如下:
typedef void (^dispatch_block_t)( void);
这意味着加入 dispatch_queue 中的 block 必须是无参数也无返回值的。

2、dispatch_queue_create 函数 的定义如下:
dispatch_queue_t  dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
该函数创建分发队列dispatch_queue。带有两个参数:一个用于标识 dispatch_queue 的字符串;一个是保留的 dispatch_queue 属性,将其设置为 NULL 即可。
3、也可使用函数 dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags);
来获得全局的 dispatch_queue,参数 priority 表示优先级,值得注意的是:我们不能修改该函数返回的 dispatch_queue。例如:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[[[self captureManager] session] startRunning];

});

4、dispatch_async 函数的定义如下:
void  dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
该函数将一个 block 加入一个 dispatch_queue,这个 block 会再其后得到调度时,并行运行。
相应的 dispatch_sync 函数就是同步执行了,一般很少用到。比如上面的代码如果我们修改为 dispatch_sync,那么就无需编写 flag 同步代码了。
5、dispatch_block_t 类型
dispatch_block_t

dispatch_queue 的运作机制及线程间同步
我们可以将许多 blocks 用 dispatch_async 函数提交到到 dispatch_queue 串行运行。这些 blocks 是按照 FIFO(先入先出)规则调度的,也就是说,先加入的先执行,后加入的一定后执行,但在某一个时刻,可能有多个 block 同时在执行。
在上面的例子中,我们的主线程一直在轮询 flag 以便知晓 block 线程是否执行完毕,这样做的效率是很低的,严重浪费 CPU 资源。我们可以使用一些通信机制来解决这个问题,如:semaphore(信号量)。 semaphore 的原理很简单,就是生产-消费模式,必须生产一些资源才能消费,没有资源的时候,那我就啥也不干,直到资源就绪。

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

initData();

// Create a semaphore with 0 resource

__block dispatch_semaphore_t sem = dispatch_semaphore_create(0);

// create dispatch semaphore

dispatch_queue_t queue = dispatch_queue_create("StudyBlocks", NULL);    dispatch_async(queue, ^(void) {

int sum = 0;

for(int i = 0; i < Length; i++)

sum += data;

NSLog(@" >> Sum: %d", sum);

// signal the semaphore: add 1 resource

dispatch_semaphore_signal(sem);

});

// wait for the semaphore: wait until resource is ready.

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

dispatch_release(sem);

dispatch_release(queue);

[pool drain];

1、dispatch_semaphore_create函数
此函数用于创建一个 __block semaphore,这里将其资源初始值设置为 0 (不能少于 0),表示任务还没有完成,没有资源可用主线程不要做事情。
2、dispatch_semaphore_signal函数
该函数 增加 semaphore 计数(可理解为资源数),表明任务完成,有资源可用主线程可以做事情了。
3、dispatch_semaphore_wait函数
主线程中的 dispatch_semaphore_wait 就是减少 semaphore 的计数,如果资源数少于 0,则表明资源还可不得,我得按照FIFO(先等先得)的规则等待资源就绪,一旦资源就绪并且得到调度了,我再执行。

下面我们来看一个按照 FIFO 顺序执行并用 semaphore 同步的例子:先将数组求和再依次减去数组。

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

initData();

__block int sum = 0;

// Create a semaphore with 0 resource

__block dispatch_semaphore_t sem = dispatch_semaphore_create(0);

__block dispatch_semaphore_t taskSem = dispatch_semaphore_create(0);

// create dispatch semaphore

dispatch_queue_t queue = dispatch_queue_create("StudyBlocks", NULL);

dispatch_block_t task1 = ^(void) {

int s = 0;

for (int i = 0; i < Length; i++)

s += data;

sum = s;

NSLog(@" >> after add: %d", sum);

dispatch_semaphore_signal(taskSem);

};

dispatch_block_t task2 = ^(void) {

dispatch_semaphore_wait(taskSem, DISPATCH_TIME_FOREVER);

int s = sum;

for (int i = 0; i < Length; i++)

s -= data;

sum = s;

NSLog(@" >> after subtract: %d", sum);

dispatch_semaphore_signal(sem);

};

dispatch_async(queue, task1);

dispatch_async(queue, task2);

// wait for the semaphore: wait until resource is ready.

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

dispatch_release(taskSem);

dispatch_release(sem);

dispatch_release(queue);

[pool drain];

在上面的代码中,我们利用了 dispatch_queue 的 FIFO 特性,确保 task1 先于 task2 执行,而 task2 必须等待直到 task1 执行完毕才开始干正事,主线程又必须等待 task2 才能干正事。 这样我们就可以保证先求和,再相减,然后再让主线程运行结束这个顺序。


使用 dispatch_apply 进行并发迭代:

对于上面的求和操作,我们也可以使用 dispatch_apply 来简化代码的编写.

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

initData();

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

__block int sum = 0;

__block int *pArray = data;

// iterations

//

dispatch_apply(Length, queue, ^(size_t i) {

sum += pArray;

});

NSLog(@" >> sum: %d", sum);

dispatch_release(queue);

[pool drain];

 
注意,这里使用了全局 dispatch_queue。
dispatch_apply 的定义如下:
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
参数 iterations 表示迭代的次数,void (^block)(size_t) 是 block 循环体。这么做与 for 循环相比有什么好处呢?答案是:并行,这里的求和是并行的,并不是按照顺序依次执行求和的。

dispatch group

我们可以将完成一组相关任务的 block 添加到一个 dispatch group 中去,这样可以在 group 中所有 block 任务都完成之后,再做其他事情。比如 6 中的示例也可以使用 dispatch group 实现:

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

initData();

__block int sum = 0;

// Create a semaphore with 0 resource

//

__block dispatch_semaphore_t taskSem = dispatch_semaphore_create(0);

// create dispatch semaphore

//

dispatch_queue_t queue = dispatch_queue_create("StudyBlocks", NULL);

dispatch_group_t group = dispatch_group_create();

dispatch_block_t task1 = ^(void) {

int s = 0;

for (int i = 0; i < Length; i++)

s += data;

sum = s;

NSLog(@" >> after add: %d", sum);

dispatch_semaphore_signal(taskSem);

};

dispatch_block_t task2 = ^(void) {

dispatch_semaphore_wait(taskSem, DISPATCH_TIME_FOREVER);

int s = sum;

for (int i = 0; i < Length; i++)

s -= data;

sum = s;

NSLog(@" >> after subtract: %d", sum);

};

// Fork

dispatch_group_async(group, queue, task1);

dispatch_group_async(group, queue, task2);

// Join

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_release(taskSem);

dispatch_release(queue);

dispatch_release(group);

[pool drain];

在上面的代码中,我们使用 dispatch_group_create 创建一个 dispatch_group_t,然后使用语句:dispatch_group_async(group, queue, task1)将 block 任务加入队列中,并与组关联,这样我们就可以使用 dispatch_group_wait(group, DISPATCH_TIME_FOREVER)来等待组中所有的 block 任务完成再继续执行。

至此我们了解了 dispatch queue 以及 block 并行编程相关基本知识,开始在项目中运用它们吧。

转:http://liwpk.blog.163.com/blog/static/363261702012413103111749/

Blocks与Dispatch Queue的使用的更多相关文章

  1. 装个蒜。学习下dispatch queue

    dispatch queue的真髓:能串行,能并行,能同步,能异步以及共享同一个线程池. 接口: GCD是基于C语言的APT.虽然最新的系统版本中GCD对象已经转成了Objective-C对象,但AP ...

  2. GCD: 基本概念和Dispatch Queue 【转】

    什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像 NSOperationQueue,他们都允 ...

  3. GCD之dispatch queue深入浅出

    GCD之dispatch queue深入浅出 http://blog.csdn.net/samuelltk/article/details/9452203

  4. GCD介绍(一): 基本概念和Dispatch Queue

    什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像NSOperationQueue,他们都允许 ...

  5. NSThread 、NSRunLoop 和 Dispatch Queue

     iOS多线程编程中,NSOperation和NSOperationQueue无疑是最常用的,它们能满足绝大部分情况下的线程操作.但在完成一些特殊的任务时,我们还是要使用的NSThread和NSRun ...

  6. GCD系列 之(一):基本概念和Dispatch Queue

    参考学习https://www.dreamingwish.com/article/grand-central-dispatch-basic-1.html系列文章,貌似也是翻译自他处的.觉得非常完整,就 ...

  7. IOS开发 GCD介绍: 基本概念和Dispatch Queue

    iOS的三种多线程技术 1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) 2.以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 ØN ...

  8. GCD 学习(三)Main&Global Dispatch Queue

    摘录自:http://zhuyanfeng.com/archives/3066 Main Dispatch Queue是在主线程中执行任务的Dispatch Queue.因为主线程只有1个,所以Mai ...

  9. GCD 学习(二)dispatch_queue_create创建Dispatch Queue

    摘录于: http://zhuyanfeng.com/archives/3042 dispatch_queue_create 用于创建用户线程队列.可以创建Serial/Concurrent Disp ...

随机推荐

  1. c 语言文本文件判断是否到达结尾的问题

    在c语言中,判断文件结尾有两种方法,第一种是使用feof()函数,feof(fp)用于测试fp所指向的文件的当前状态是否为“文件结束”.如果是,函数则返回的是非0值(真),否则为0(假),要注意的是, ...

  2. HTTP.Socket.TCP详解

    这会没事,整理了一下HTTP,socket,TCP之间的关系与区别,我们在面试的时候应该会经常问到这方面的东西,那么什么是HTTP呢? HTTP属于老话题了,在项目中我们经常需要往服务端发POST或者 ...

  3. 在PHP中gmtime()与time()区别

    localtime是把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间,而gmtime函数转换后的时间没有经过时区变换,是UTC时间.2.说明:此函数获得的tm结构体的时间是日历 ...

  4. react篇章-React State(状态)-组件都是真正隔离的

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title&g ...

  5. 绝对良心的 Java 中发邮件功能

    开篇语,是不是感觉这个功能都老掉牙了,网上一大推的文章,随便找个代码就是了,为什么我还要选择专门写一篇呢,因为我遇到了不一样的坑…… 首先,不免俗套的把代码都贴上来,拿去执行吧,记住换上你的账号和授权 ...

  6. BZOJ3589动态树

    **错误改了一上午. 先做熟练泼粪 k<=5,因此我们可以模拟这个过程,在线段树上把标记建出来然后pushup时候更新就好了. By:大奕哥 #include<bits/stdc++.h& ...

  7. 【持续更新】NOIP注意事项

    1.无根据的乱搞不可能对 2.必须造极限数据跑一下 3.必须测空间 4.不管用不用都把cstring加上 5.开文件测样例 6.删一长串代码最好注释 7.到10:00先敲暴力 8.题读三遍 9.先做好 ...

  8. 王彪-20162321-Java程序设计与数据结构2nd-第十周学习总结

    学习目标 讨论有向图和无向图 定义带权图并讨论它们的应用 定义图的广度优先遍历和深度优先遍历 定义最小生成树 讨论图的实现策略 书中图的基本定义 图是由结点及结点间的连接组成的,结点称为顶点,结点间的 ...

  9. hdu 4536 dfs

    题意:XCOM-Enemy Unknown是一款很好玩很经典的策略游戏.在游戏中,由于未知的敌人--外星人入侵,你团结了世界各大国家进行抵抗.随着游戏进展,会有很多的外星人进攻事件.每次进攻外星人会选 ...

  10. Java混剪音频

    分享一个之前看过的程序,可以用来剪辑特定长度的音频,将它们混剪在一起,思路如下: 1.使用 FileInputStream 输入两个音频 2.使用 FileInputStream的skip(long ...