UIButton * btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

[btn setTitle:@"default"forState:UIControlStateNormal];

btn.backgroundColor = [UIColorblackColor];

[self.view addSubview:btn];

//情况一

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

for( NSInteger i = 0; i < 20; ++ i )

{

sleep(1);

NSLog(@"loop:%d",i);

dispatch_async(dispatch_get_main_queue(), ^{

NSString * str = [NSString stringWithFormat:@"%d",i];

[btn setTitle:str forState:UIControlStateNormal];

});

}

});

//情况二

/*

dispatch_queue_t serQ = dispatch_queue_create("rock", DISPATCH_QUEUE_SERIAL);

dispatch_async(serQ, ^{

for( NSInteger i = 0; i < 20; ++ i )

{

sleep(1);

NSLog(@"loop:%d",i);

dispatch_async(dispatch_get_main_queue(), ^{

NSString * str = [NSString stringWithFormat:@"%d",i];

[btn setTitle:str forState:UIControlStateNormal];

});

}

});

*/

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

当某几个任务执行完毕之后,接着执行另外一个任务,那就应该使用dispatch group 这个功能了

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{

for( NSInteger i = 0; i < 10; ++ i )

{

sleep(1);

NSLog(@"loop1:%d",i);

}

});

dispatch_group_async(group,dispatch_get_global_queue(0, 0),^{

for( NSInteger i = 0; i < 10; ++ i )

{

sleep(1);

NSLog(@"loop2:%d",i);

}

});

dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{

NSLog(@"done");

});

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

dispatch_barrier_async

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
}); dispatch_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
}); dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});

如果像上面那样简单的在dispatch_async方法中添加写入数据处理的任务,那么根据Concurrent Dispatch Queue并行执行的性质,就很有可能不是按照上面的添加处理任务的

顺序执行,那么在blk3_reading 和 blk4_reading执行读取数据的时候,blk1_writting进行写入数据的处理还没有执行到,那么后两次的读取数据操作读取到的数据就与期望中

的不符了。

解决这个问题的处理就是 使用 dispatch_barrier_async

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
}); dispatch_barrier_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
}); dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});

使用dispatch_barrier_async方法,它会等待在它之前添加到 Concurrent Dispatch Queue的所有处理执行结束后,才执行该处理,然后等待该处理结束后,才接着处理后续添

加到Concurrent Dispatch Queue中的处理任务。当然在dispatch_barrier_async方法之前和之后添加的处理任务可以并发执行,即不保证执行顺序,但是可以确保

dispatch_barrier_async方法添加的任务一定是只能同时执行一个,按其添加任务顺序执行的,就是说,执行完blk1_reading和blk2_reading的读取数据任务后,才是进行

blk1_writting的写入数据任务,然后才是执行接着的读取数据的任务。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

一、GCD是异步执行任务的技术之一,一般将应用程序中记叙的线程管理用的代码在系统级中是实现。

开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。

二、多线程编程

由于使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此看上去就好像一个CPU核能够并行的执行多个线程一样,而且在具有多个CPU核的情况下,就不是“看上去像”了,而是真的提供了多个CPU核并行执行任务的多个线程的技术。

多线程编程容易发生各种编程问题:比如多个线程更新相同的资源会导致数据的不一致(数据竞争)、停止等待事件的线程会导致多个线程相互持续等待(死锁)、使用太多线程会消耗大量的内存资源等等。

尽管会有问题,但是为什么还要用呢?

在应用程序启动时,通过最先执行的线程,即主线程来描绘用户界面、处理用户触摸事件等,但是如果在该主线程中进行长时间的处理,就会妨碍主线程中被称为RunLoop的主循环的执行,从而导致不能跟新用户界面,应用程序的画面长时间停滞等问题。

三、GCD 的 API

1.Dispatch Queue

开发者要做的只是定义想要执行的任务并追加到适当的DispatchQueue中。

在Block中定义想要执行的任务,然后追加到Dispatch Queue中

Dispatch Queue是执行处理的等待队列,通过dispatch_async等API,在Block语法中记叙想要执行的处理并将其追加到Dispatch Queue中,Dispatch Queue按照追加的顺序(FIFO)执行处理。

另外,在执行处理时存在两种Dispatch Queue:

Dispatch Queue的种类

说明

Serial Dispatch Queue

等待现在执行中处理结束

Concurrent Dispatch Queue

不等待现在执行中处理结束

解释说明一下:

(1)         Serial Dispatch Queue:就是要等待现在执行中处理结束后才可以进行下一个任务的执行处理,假如现在有blk1,blk2,blk3,在Serial Dispatch Queue中,那么同时执行处理数只能是一个,而且按按添加顺序FIFO进行处理,即先执行blk1,执行结束后再执行blk2,执行结束再进行blk3的执行。

(2)         Concurrent Dispatch Queue:就是一个线程的执行不等待现在(当前)执行中的任务处理结束就可以开始另一个任务的执行处理。同样假如有blk1,blk2,blk3在Concurrent Dispatch Queue中,那么首先执行blk1,不管blk1是否执行处理结束,都开始执行后面的blk2,不管blk2是否执行结束,都开始执行后面的blk2。

这样虽然不用等待处理结束,可以并行执行多个任务处理,但是并行处理数取决于当前系统的状态,有它决定Concurrent Dispatch Queue中并行执行的处理数。所谓并行执行就是使用多个线程来同时执行多个处理任务(block中的执行任务)。

SerialDispatchQueue同时只能执行一个追加处理

ConcurrentDispatchQueue并行执行多个追加处理

虽然SerialDispatchQueue ConcurrentDispatchQueue受到系统资源的限制,但是用dispatch_queue_create可以生成任意多个Dispatch Queue

当生成多个SerialDispatchQueue时,各个SerialDispatchQueue将并行执行,虽然一个SerialDispatchQueue同时只能执行一个追加处理,但是如果将处理分别追加到4个

Serial Dispatch Queue中,各个Serial Dispatch Queue执行一个,即为同时执行4个处理

但是生成Serial Dispatch Queue的个数受系统限制

为了避免多线程编程的问题之一—数据竞争,就可以使用Serial Dispatch Queue。

当想并行执行且不发生数据竞争等问题时就应该使用Concurrent Dispatch Queue。

//以下代码是两种生成Serial Dispatch Queue的方式
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", NULL);
// dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);

dispatch_queue_create,该方法中的第一个参数指定SerialDispatchQueue的名称,DispatchQueue的名称推荐使用应用程序ID之中逆序全程域名,第二个参数指定为NULL(或者DISPATCH_QUEUE_SERIAL)时即表示生成的是Serial Dispatch Queue,指定为DISPATCH_QUEUE_CONCURRENT时即表示生成的是Concurrent Dispatch Queue

//以下代码生成ConcurrentDispatchQueue
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("cn.edu.scnu.myConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(myConcurrentDispatchQueue, ^{
NSLog(@"block on my ConcurrentDispatchQueue");
});

2、MainDispatch Queue 和 Global Dispatch Queue

实际上不用特意生成DispatchQueue,系统也会提供几个给我们,就是Main DispatchQueue 和 Global Dispatch Queue

Main Dispatch Queue就是主线程中执行的Dispatch Queue,因为主线程只有一个,所以Main DispatchQueue自然就是 Serial Dispatch Queue

//获取Main Dispatch Queue
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

GlobalDispatch Queue是所有应用程序都能够使用的Concurrent Dispatch Queue,没有必要通过dispatch_queue_create方法逐个生成Concurrent Dispatch Queue,只要获

取GlobalDispatch Queue使用即可。

Global Dispatch Queue有四个优先级:High Priority,Default Priority,Low Priority,Background Priority

 //获取高优先级的的Global Dispatch Queue
/*
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
*/
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//第一个参数指定Global Dispatch Queue的优先级,第二个参数指定为0

3、dispatch_set_target_queue

dispatch_queue_create函数生成的Dispatch Queue不管是Serial Dispatch Queue还是Concurrent Dispatch Queue,都是使用与默认优先级的Global Dispatch Queue相同执

行优先级的线程

如果想变更生成的Dispatch Queue的执行优先级要使用dispatch_set_target_queue方法。

dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueue);
//指定要变更执行优先级的dispatch queue为dispatch_set_target_queue方法的第一个参数,指定与要使用的执行优先级相同优先级的Global Dispatch Queue为第二个参数(目标)
//第一个参数不可以指定为系统提供的Main Dispatch Queue 和 Global Dispatch Queue

4、dispatch_after

想在指定时间后执行处理的情况,可以使用 dispatch_after 方法来实现

 double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(@"waited at least 2.0 seconds.");
});

值得注意的是,dispatch_after方法并不是在指定时间后执行处理任务,而是在指定时间后追加处理到dispatch queue中,上面的代码在2秒后用dispatch_after方法追加

block到Main Dispatch Queue中,因为Main Dispatch Queue在主线程的RunLoop中执行,所以比如每个1/60秒执行的RunLoop中,Block最快在2秒后执行,最慢在2秒+1/60

秒后执行,而且在Main Dispatch Queue中又大量处理追加或者主线程的处理本身有延时时,这个时间会更长。

dispatch_after这个方法的第二个参数指定要追加的dispatch queue,第三个参数指定要执行处理的Block,第一个参数是指定时间用的dispatch_time_t类型的值,在使用

dispatch_after的时候,编译器会自动帮你生成这些代码,只需修改delayInSeconds就可以了。

5、Dispatch Group

在追加到dispatch queue中的多个处理全部结束后想执行结束处理任务,这种情况会经常出现。只使用一个Serial Dispatch Queue时,只要将想执行的全部处理都追加到该

Serial Dispatch Queue种并在最后追加结束处理就可以实现。但是在使用Concurrent Dispatch Queue时或者同时使用多个dispatch queue时,就会有些复杂了,在这种情况下

就应该使用Dispatch Group

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");}); dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});

因为向 Global Dispatch Queue即Concurrent Dispatch Queue追加处理任务,多个线程并行执行,所以追加处理任务的执行顺序是不定的,执行时顺序会发生变化,但是主线程中执行结果输出done肯定是最后的。

下面稍微解释一下上面的那段代码,上面由3个输出任务的block组成一个dispatch group,并把这个dispatch group添加到dispatch queue中执行,当dispatch
group中的

block任务执行完毕后,dispatch_group_notify方法就会被执行到,所以它的第一个参数是group,表示其被监视。在追加到dispatch group中的全部执行处理任务执行结束后,

将第三个参数中的block任务添加到第二个参数的dispatch queue中执行,注意此时dispatch group中的所以执行任务已经执行结束了。

另外,在dispatch group中也可以使用 dispatch_group_wait方法仅等待全部处理执行结束。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");}); dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

在这个方法中的第二个参数指定等待时间,这里使用DISPATCH_TIME_FOREVER意味着永久等待,只要属于dispatch group中的处理尚未执行结束,就会一直等待,中途不能取消。

当然如同dispatch——after方法中那样,也可以指定等待时间为1秒等等。

//这里指定等待时间1s,即1s后查看dispatch group中的处理是否全部执行结束
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == 0) {
//属于dispatch group中的全部处理都执行结束
}
else {
//属于dispatch group的某一个处理还在执行
}
 //这里也可以指定DISPATCH_TIME_NOW,则不用任何等待即可判断属于dispatch group中的处理是否全部执行结束
long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);

6、dispatch_barrier_async

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
}); dispatch_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
}); dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});

如果像上面那样简单的在dispatch_async方法中添加写入数据处理的任务,那么根据Concurrent Dispatch Queue并行执行的性质,就很有可能不是按照上面的添加处理任务的

顺序执行,那么在blk3_reading 和 blk4_reading执行读取数据的时候,blk1_writting进行写入数据的处理还没有执行到,那么后两次的读取数据操作读取到的数据就与期望中

的不符了。

解决这个问题的处理就是 使用 dispatch_barrier_async

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
}); dispatch_barrier_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
}); dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});

使用dispatch_barrier_async方法,它会等待在它之前添加到 Concurrent Dispatch Queue的所有处理执行结束后,才执行该处理,然后等待该处理结束后,才接着处理后续添

加到Concurrent Dispatch Queue中的处理任务。当然在dispatch_barrier_async方法之前和之后添加的处理任务可以并发执行,即不保证执行顺序,但是可以确保

dispatch_barrier_async方法添加的任务一定是只能同时执行一个,按其添加任务顺序执行的,就是说,执行完blk1_reading和blk2_reading的读取数据任务后,才是进行

blk1_writting的写入数据任务,然后才是执行接着的读取数据的任务。

7、dispatch——sync

dispatch_async方法中的async意味着“非同步”,就是将指定的block非同步的添加到dispatch qeueue中,dispatch_async方法不做任何等待。

dispatch_sync方法中的sync意味着“同步”,也就是将指定的block同步追加到dispatch queue中,在追加block的过程结束之前,dispatch_sync方法会一直等待。

一旦调用dispatch_sync,那么在指定的处理执行结束之前,该方法不会返回,dispatch_sync方法可以简化代码,也可以说是简易版的dispatch_group_wait方法

dispatch_sync方法使用简单,但是容易引起死锁

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"引起死锁!");
});

上面的代码在主线程中执行指定的block,并等待其执行结束,但是其实在主线程中就是在执行这些代码,所以就造成了死锁。

IOS GCD的更多相关文章

  1. iOS GCD基础篇 - 同步、异步,并发、并行的理解

    1.关于GCD - GCD全称是Grand Central Dispatch  - GCD是苹果公司为多核的并行运算提出的解决方案  - GCD会自动利用更多的CPU内核(比如双核.四核)  - GC ...

  2. iOS GCD之dispatch_semaphore(信号量)

    前言 最近在看AFNetworking3.0源码时,注意到在 AFURLSessionManager.m 里面的 tasksForKeyPath: 方法 (L681),dispatch_semapho ...

  3. iOS GCD 编程小结

    一.简单介绍 1.GCD简介? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD优势 GCD是苹果公司为多核的并行运算提出的 ...

  4. iOS GCD NSOperation NSThread等多线程各种举例详解(拷贝)

    2年多的iOS之路匆匆而过,期间也拜读来不少大神的博客,近来突然为自己一直做伸手党感到羞耻,是时候回馈社会.回想当年自己还是小白的时候,照着一些iOS多线程教程学,也只是照抄,只知其然.不知其所以然. ...

  5. ios - GCD简单小结

    首先GCD两个名词: 队列 同步异步. 队列: 任务放到队列,队列中的任务执行方式取决于执行队列中任务的方式---同步异步. 串行队列: 任务顺序执行,可以叫阻塞队列.只有前面任务完成才执行后面的. ...

  6. iOS GCD 与 NSOperationQueue

    NSOperationQueue ios NSOperation vs. GCD StackOverflow: NSOperation vs. Grand Central Dispatch Blog: ...

  7. IOS GCD 使用 (二)

     上一节,主要介绍了GCD的基本的概念,这节将用代码深入详细介绍GCD的使用. 一  使用介绍    GCD的使用主要分为三步:创建代码块;选择或创建合适的分发队列;(同步.异步方式)向分发队列提交任 ...

  8. iOS——GCD多线程

    1> 概述 Grand Central Dispatch (GCD)是Apple开发的一种多核编程技术.主要用于优化应用程序以支持多核处理器以及其他对称多处理系统. GCD提供函数实现多线程开发 ...

  9. IOS GCD 的理解

    GCD (Grand Central Dispatch) 是Apple公司开发的一种技术,它旨在优化多核环境中的并发操作并取代传统多线程的编程模式. 在Mac OS X 10.6和IOS 4.0之后开 ...

  10. IOS GCD使用实例大全

    GCD是大家在IOS开发过程中经常使用的一种多线程管理机制.原理这里就不多说了,大家关心的大部分都是它的使用,下面主要介绍GCD的主要方法及其实例. 1.认识主队列,感受串行队列的运行,运行结果打印的 ...

随机推荐

  1. [主席树]HDOJ3874 Necklace

    题意:n个数 m个询问 询问的是[l, r]区间内不同的数的和 没有修改,静态的主席树即可 与 SPOJ QUERY 一样 将重复的元素建树即可 注意范围:$N \le  50000$ 每个值不超过1 ...

  2. 使用getScript()方法异步加载并执行js文件

    使用getScript()方法异步加载并执行js文件 使用getScript()方法异步请求并执行服务器中的JavaScript格式的文件,它的调用格式如下所示: jQuery.getScript(u ...

  3. java.util.concurrent包API学习笔记

    newFixedThreadPool 创建一个固定大小的线程池. shutdown():用于关闭启动线程,如果不调用该语句,jvm不会关闭. awaitTermination():用于等待子线程结束, ...

  4. Unity UGUI —— 无限循环List(转载)

    using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; ...

  5. C#基础(WinForm窗体的单例模式,避免窗体被实例化多次)

    在MDI窗体中使用单例模式可以有效的避免同个窗体被实例化多次 [csharp] view plain copy   //==============字窗体的修改================  // ...

  6. 254. Factor Combinations

    题目: Numbers can be regarded as product of its factors. For example, 8 = 2 x 2 x 2; = 2 x 4. Write a ...

  7. Android LayoutInflater.from(context).inflate

    在应用中自定义一个view,需要获取这个view的布局,需要用到 (LinearLayout) LayoutInflater.from(context).inflate(R.layout.conten ...

  8. jquery index()方法

    搜索匹配的元素,并返回相应元素的索引值,从0开始计数. 如果不给 .index() 方法传递参数,那么返回值就是这个jQuery对象集合中第一个元素相对于其同辈元素的位置.        如果参数是一 ...

  9. oracle lsnrctl status|start|stop

    [oracle@redhat4 ~]$ lsnrctl status LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 06-OCT-2015 ...

  10. Android之界面刷新(invalidate和postInvalidate使用)

    Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用. Android提供了Inva ...