社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
全名:Grand Central Dispatch
,它是苹果为多核的并行运算提出的解决方案,会合理利用CPU、自动管理线程的生命周期。使用时只需要在Block中写入需要执行的代码即可。使用非常灵活。
主队列:用于刷新UI,任何需要刷新UI的工作都要放在主队列中执行,同时为防止UI卡住,一般需要把耗时的任务放在其他线程中执行
dispatch_queue_t queue = dispatch_get_main_queue();
全局队列:一般并行任务都加入这个队列中,这是系统提供的一个全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
获取程序进程缺省产生的并发队列,可根据优先级来选择高、中、低三个优先级,
由于这个是全局有系统控制的队列,所以我们无法对其进行 dispatch_resume() 继续 和 dispatch_suspend() 中断。
自定义队列:可以自定义串行队列或者并行队列
//串行队列
dispatch_queue_t queue = dispatch_queue_create("demo", NULL);
dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空
第二个参数用来表示创建的队列是串行的还是并行的,传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列
第一种:
-(void)test1{
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
输出:
2018-02-23 16:12:36.353985+0800 GCDDemo[24815:1940432] 1
会卡在
dispatch_sync(dispatch_get_main_queue(), ^{
,或者报异常
由于是主队列同步执行而且block是后下入主队列的,所以block会放到主队列的后面等待主队列执行完毕后再执行,所以2是放在3的后面的。但是主线程也在等block执行完毕,这样主线程才会继续执行。也就是说3又在等2执行完毕才会执行。所以出现了死锁
第二种:
-(void)test2{
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2");
});
NSLog(@"3");
}
输出:
2018-02-23 16:17:15.853493+0800 GCDDemo[24879:1950825] 1
2018-02-23 16:17:15.853623+0800 GCDDemo[24879:1950825] 2
2018-02-23 16:17:15.853715+0800 GCDDemo[24879:1950825] 3
本次为同步全局队列。虽然主线程队列会等待2的执行,但是2这次没有放在3的后面而是在另一个全局队列中,所以不会造成死锁。
第三种:
-(void)test3{
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
输出:
2018-02-23 16:21:25.714923+0800 GCDDemo[24938:1961169] 1
2018-02-23 16:21:25.715117+0800 GCDDemo[24938:1961169] 5
2018-02-23 16:21:25.715124+0800 GCDDemo[24938:1961306] 2
2018-02-23 16:21:25.718781+0800 GCDDemo[24938:1961169] 3
2018-02-23 16:21:25.718926+0800 GCDDemo[24938:1961306] 4
这里看似和第一种类似好像会造成死锁,其实不是的,这次主队列其实已经运行完
第四种:
-(void)test4{
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
while (YES) {
}
NSLog(@"6");
}
输出:
2018-02-23 16:26:49.901088+0800 GCDDemo[25017:1973298] 1
2018-02-23 16:26:49.901234+0800 GCDDemo[25017:1973298] 5
2018-02-23 16:26:49.901246+0800 GCDDemo[25017:1973398] 2
主线程没有执行完,6没打印,所以3还是放在主线程队列后面,但是主线程没有执行完,不会执行3。而3是同步的,所以3不执行完4也不会打印
-(void)test7{
//并发同步
NSLog(@"start");
dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"1----:%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"2----:%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"3----:%@",[NSThread currentThread]);
}
});
NSLog(@"end");
}
输出:
2018-02-23 16:36:18.856563+0800 GCDDemo[25130:1996747] start
2018-02-23 16:36:18.856797+0800 GCDDemo[25130:1996747] 1----:<NSThread: 0x600000077040>{number = 1, name = main}
2018-02-23 16:36:18.856986+0800 GCDDemo[25130:1996747] 1----:<NSThread: 0x600000077040>{number = 1, name = main}
2018-02-23 16:36:18.857095+0800 GCDDemo[25130:1996747] 2----:<NSThread: 0x600000077040>{number = 1, name = main}
2018-02-23 16:36:18.857388+0800 GCDDemo[25130:1996747] 2----:<NSThread: 0x600000077040>{number = 1, name = main}
2018-02-23 16:36:18.857588+0800 GCDDemo[25130:1996747] 3----:<NSThread: 0x600000077040>{number = 1, name = main}
2018-02-23 16:36:18.857749+0800 GCDDemo[25130:1996747] 3----:<NSThread: 0x600000077040>{number = 1, name = main}
2018-02-23 16:36:18.857875+0800 GCDDemo[25130:1996747] end
-(void)test8{
//并发异步
NSLog(@"start");
dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"1----:%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"2----:%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"3----:%@",[NSThread currentThread]);
}
});
NSLog(@"end");
}
输出:
2018-02-23 16:38:26.222795+0800 GCDDemo[25170:2002606] start
2018-02-23 16:38:26.222964+0800 GCDDemo[25170:2002606] end
2018-02-23 16:38:26.223061+0800 GCDDemo[25170:2002744] 1----:<NSThread: 0x600000267700>{number = 3, name = (null)}
2018-02-23 16:38:26.223092+0800 GCDDemo[25170:2002747] 2----:<NSThread: 0x600000267640>{number = 4, name = (null)}
2018-02-23 16:38:26.223104+0800 GCDDemo[25170:2002745] 3----:<NSThread: 0x604000465b00>{number = 5, name = (null)}
2018-02-23 16:38:26.223231+0800 GCDDemo[25170:2002744] 1----:<NSThread: 0x600000267700>{number = 3, name = (null)}
2018-02-23 16:38:26.223389+0800 GCDDemo[25170:2002747] 2----:<NSThread: 0x600000267640>{number = 4, name = (null)}
2018-02-23 16:38:26.223480+0800 GCDDemo[25170:2002745] 3----:<NSThread: 0x604000465b00>{number = 5, name = (null)}
-(void)test9{
//串行同步
NSLog(@"start");
dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"1----:%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"2----:%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"3----:%@",[NSThread currentThread]);
}
});
NSLog(@"end");
}
输出:
2018-02-23 16:42:13.941307+0800 GCDDemo[25237:2012245] start
2018-02-23 16:42:13.941525+0800 GCDDemo[25237:2012245] 1----:<NSThread: 0x600000065680>{number = 1, name = main}
2018-02-23 16:42:13.941707+0800 GCDDemo[25237:2012245] 1----:<NSThread: 0x600000065680>{number = 1, name = main}
2018-02-23 16:42:13.941940+0800 GCDDemo[25237:2012245] 2----:<NSThread: 0x600000065680>{number = 1, name = main}
2018-02-23 16:42:13.942073+0800 GCDDemo[25237:2012245] 2----:<NSThread: 0x600000065680>{number = 1, name = main}
2018-02-23 16:42:13.942181+0800 GCDDemo[25237:2012245] 3----:<NSThread: 0x600000065680>{number = 1, name = main}
2018-02-23 16:42:13.942279+0800 GCDDemo[25237:2012245] 3----:<NSThread: 0x600000065680>{number = 1, name = main}
2018-02-23 16:42:13.942372+0800 GCDDemo[25237:2012245] end
-(void)test10{
//串行异步
NSLog(@"start");
dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"1----:%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"2----:%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"3----:%@",[NSThread currentThread]);
}
});
NSLog(@"end");
}
输出:
2018-02-23 16:43:05.320128+0800 GCDDemo[25263:2014958] start
2018-02-23 16:43:05.320452+0800 GCDDemo[25263:2014958] end
2018-02-23 16:43:05.320553+0800 GCDDemo[25263:2015026] 1----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
2018-02-23 16:43:05.321290+0800 GCDDemo[25263:2015026] 1----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
2018-02-23 16:43:05.322392+0800 GCDDemo[25263:2015026] 2----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
2018-02-23 16:43:05.322896+0800 GCDDemo[25263:2015026] 2----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
2018-02-23 16:43:05.323378+0800 GCDDemo[25263:2015026] 3----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
2018-02-23 16:43:05.323637+0800 GCDDemo[25263:2015026] 3----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2-------%@",[NSThread currentThread]);
});
});
dispatch_barrier_async
我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。
- (void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
可以用来处理 读者与写者问题
dispatch_after
当我们需要延迟执行一段代码时,就需要用到GCD的dispatch_after方法。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
NSLog(@"run-----");
});
dispatch_once
我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once方法。使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
dispatch_apply
通常我们会用for循环遍历,但是GCD给我们提供了快速迭代的方法dispatch_apply,使我们可以同时遍历。比如说遍历0~5这6个数字,for循环的做法是每次取出一个元素,逐个遍历。dispatch_apply可以同时遍历多个数字。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd------%@",index, [NSThread currentThread]);
});
dispatch_group
有时候我们会有这样的需求:分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的队列组。
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的操作
NSLog(@"耗时操作");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的操作
NSLog(@"耗时操作");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
也可以使用
dispatch_group_enter
和dispatch_group_leave
来通知 Dispatch Group 任务开始和完成,同时需要注意dispatch_group_enter
和dispatch_group_leave
是成对出现。
在多线程中一个对象的数据被同时读取和写入将会导致数据不可信。例如,多线程可以同时读取NSMutableArray
的一个实例而不会产生问题,但是当一个线程正在读取时,另一个线程正在修改数据就是不安全的。
//写入
-(void)addObj:(id)obj{
if(obj){
[_objArray addObject:obj];
}
}
//读取
-(NSArray *)array{
return [NSArray arrayWithArray:_objArray];
}
这里所谓的读方法,它读取可变数据。它为调用者生成一个不可变的拷贝,防止调用者不当的改变数组,但是这不能提供任何保护来对抗一个线程调用读方法的同时另一个线程调用写方法。
GCD通过用dispatch barriers
创建一个读写锁提供解决方案。
Dispatch barriers 是一组函数,在并发队列上工作时扮演一个串行式的瓶颈。使用 GCD 的障碍(barrier)API 确保提交的 Block 在那个特定时间上是指定队列上唯一被执行的条目。这就意味着所有的先于调度障碍提交到队列的条目必能在这个 Block 执行前完成。
当这个 Block 的时机到达,调度障碍执行这个 Block 并确保在那个时间里队列不会执行任何其它 Block 。一旦完成,队列就返回到它默认的实现状态。 GCD 提供了同步和异步两种障碍函数。
下面是你何时会——和不会——使用障碍函数的情况:
由于上面唯一像样的选择是自定义并发队列,你将创建一个你自己的队列去处理你的障碍函数并分开读和写函数。且这个并发队列将允许多个多操作同时进行。
@interface SomeObject ()
@property (nonatomic,strong,readonly) NSMutableArray *objArray;
@property (nonatomic, strong) dispatch_queue_t concurrentPhotoQueue; ///< Add this
@end
-(id)init{
...
_concurrentPhotoQueue = dispatch_queue_create("demo",
DISPATCH_QUEUE_CONCURRENT);
}
注意此处使用的是并发队列。如果改为串行队列,只能是单读、单写操作,在效率上会有折扣。
修改写入函数
-(void)addObj:(id)obj
{
if (obj) { // 1
dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2
[_objArray addObject:obj]; // 3
});
}
}
新写的函数是这样工作的:
修改读取函数
-(NSArray *)array
{
__block NSArray *retArray; // 1
dispatch_sync(self.concurrentPhotoQueue, ^{ // 2
retArray = [NSArray arrayWithArray:_objArray]; // 3
});
return retArray;
}
按顺序看看编过号的注释,有这些:
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!