iOS多线程——GCD
最近的项目遇到了很多多线程的问题,借此机会对GCD进行了一番学习并总结。首先说一下什么是GCD,GCD全称 Grand Central Dispatch,是异步执行任务的技术之一。开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。说到多线程,我们有必要了解一下GCD的两个核心概念。
一、任务和队列
(1)任务:执行什么操作
(2)队列:用来存放任务
将任务添加到队列中,GCD会自动将队列中的任务取出放到对应的线程中执行,遵循FIFO原则,先进先出,后进后出。
队列分为串行和并行,任务的执行又分为同步和异步。两两组合,串行同步,串行异步,并行同步,并行异步。而异步是多线程的代名词,异步在实际引用中会开启新的线程执行耗时操作。队列只负责任务的调度,而不负责任务的执行,任务是在线程中执行。
二、同步异步,串行并行
(1)同步异步指的是能否开启新的线程。同步不能开启新的线程,异步可以开启。
(2)串行并行指的是任务的执行方式。串行是指多个任务时,各个任务按顺序执行,完成一个后才能进行下一个。并行是指多个任务可以同时执行。(异步是多个任务并行的前提条件!)
三、GCD的API
(1)Main Dispatch Queue
是在主线程中执行任务的Dispatch Queue。因为主线程只有1个,所以Main Dispatch Queue是Serial Dispatch Queue。追加到Main Dispatch Queue中的任务将在主线程的RunLoop中执行。因为是在主线程中执行,所以应该只将用户界面更新等一些必须在主线程中执行的任务追加到Main Dispatch Queue中。
(2)Global Dispatch Queue
是所有应用程序都能使用的Concurrent Dispatch Queue。大多数情况下,可以不必通过dispatch_queue_create函数生成Concurrent Dispatch Queue,而是只要获取Global Dispatch Queue使用即可。Global Dispatch Queue有4个优先级,分别是:High、Default、Low、Background。
(3)dispatch_set_target_queue
dispatch_queue_t mySerailDispatchQueue = dispatch_queue_create("com.example.MySerailDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, );
dispatch_set_target_queue(mySerailDispatchQueue, globalDispatchQueueBackground);
    // 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样
一般都是把一个任务放到一个串行的queue中,如果这个任务被拆分了,被放置到多个串行的queue中,但实际还是需要这个任务同步执行,那么就会有问题,因为多个串行queue之间是并行的。
    dispatch_queue_t mySerailDispatchQueue = dispatch_queue_create("com.example.MySerailDispatchQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
    dispatch_set_target_queue(queue1, mySerailDispatchQueue);
    dispatch_set_target_queue(queue2, mySerailDispatchQueue);
    dispatch_set_target_queue(queue3, mySerailDispatchQueue);
    dispatch_async(queue1, ^{
        NSLog(@"1 in");
        [NSThread sleepForTimeInterval:.f];
        NSLog(@"1 out");
    });
    dispatch_async(queue2, ^{
        NSLog(@"2 in");
        [NSThread sleepForTimeInterval:.f];
        NSLog(@"2 out");
    });
    dispatch_async(queue3, ^{
        NSLog(@"3 in");
        [NSThread sleepForTimeInterval:.f];
        NSLog(@"3 out");
    });
输出结果:
-- ::59.424 GCD-test[:] in
-- ::02.427 GCD-test[:] out
-- ::02.427 GCD-test[:] in
-- ::04.431 GCD-test[:] out
-- ::04.432 GCD-test[:] in
-- ::05.437 GCD-test[:] out
通过打印的结果说明我们设置了queue1和queue2队列以targetQueue队列为参照对象,那么queue1和queue2中的任务将按照targetQueue的队列处理。
适用场景:
一般都是把一个任务放到一个串行的queue中,如果这个任务被拆分了,被放置到多个串行的queue中,但实际还是需要这个任务同步执行,那么就会有问题,因为多个串行queue之间是并行的。这时候dispatch_set_target_queue将起到作用。
(4)dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull *NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at least three seconds.");
});
该方法的第一个参数time,是指定时间用的dispatch_time_t类型的值,该值使用dispatch_time函数或dispatch_walltime函数生成。
dispatch_time函数能够获取从第一个参数指定的时间开始到第二个参数指定的毫微秒单位时间后的时间。DISPATCH_TIME_NOW表示现在的时间。dispatch_time函数通常用于计算相对时间,而dispatch_walltime函数用于计算绝对时间。
第二个参数dispatch queue,第三个参数要执行的block。需要注意的是,dispatch_after函数并不是在指定时间后执行处理,而只是在指定时间追加处理到dispatch queue。此代码与3秒后用dispatch_async函数追加block到Main Dispatch queue相同。
下面举一个简单的例子,看一下dispatch_after的执行效果:
- (void)dispatch_after
{
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 10ull *NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"hello");
});
_count = ;
_timer = [NSTimer timerWithTimeInterval: target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
} - (void)run{
if (_count == ){
[_timer invalidate];
}
_count ++;
NSLog(@"time: %ld",(long)_count);
}
-- ::27.015 GCD-test[:] time:
-- ::28.015 GCD-test[:] time:
-- ::29.014 GCD-test[:] time:
-- ::30.014 GCD-test[:] time:
-- ::31.015 GCD-test[:] time:
-- ::32.015 GCD-test[:] time:
-- ::33.015 GCD-test[:] time:
-- ::34.014 GCD-test[:] time:
-- ::35.015 GCD-test[:] time:
-- ::36.014 GCD-test[:] time:
-- ::36.015 GCD-test[:] hello
-- ::37.014 GCD-test[:] time:
(5)dispatch group
在追加到dispatch queue中的多个处理全部结束后想执行结束处理,这种情况会经常出现,当然,我们可以使用一个serial dispatch queue,将想要执行的处理全部追加到该dispatch queue中,在最后追加结束处理即可实现,但是在使用concurrent dispatch queue时或者同时使用多个dispatch queue时,就会变得颇为复杂,这个时候我们可以使用dispatch group,无论向什么样的dispatch queue中追加处理,使用dispatch group 都可监视这些处理执行的结束。一旦检测到所有处理执行结束,就可将结束的处理追加到dispatch queue中,这就是使用dispatch queue的原因。
先上代码:
- (void)dispatch_group
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{
NSLog(@"blk0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk2");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
}
-- ::49.038 GCD-test[:] blk0
-- ::49.038 GCD-test[:] blk2
-- ::49.038 GCD-test[:] blk1
-- ::49.045 GCD-test[:] done
dispatch_group_async函数与dispatch_async函数相同,都追加到Block到指定的dispatch queue中。不同的是指定生成的dispatch group为第一个参数,指定的Block属于指定的dispatch group。
在追加到dispatch group中的处理全部执行结束时,上面的源代码中使用dipatch_group_notify函数会将执行的block追加到dispach queue中,将第一个参数指定为要监视的dispatch group,在追加到该dispatch group的处理全部结束时,将第三个参数的block追加到第二个参数指定的dispatch queue。另外需要强调的是在dispatch_group_notify函数中不管制定了什么样的dispatch queue,属于dispatch group的全部处理在追加指定的block时都已经执行结束。
另外dispatch group中还可以使用dispatch_group_wait函数仅等待全部处理执行结束,还是直接上代码:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"blk0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk2");
});
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull *NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == ){
/*
* 属于dispatch group的全部处理执行结束
*/
NSLog(@"done");
}else{
/*
* 属于dispatch group的处理为全部结束
*/
NSLog(@"unfinished");
}
-- ::47.378 GCD-test[:] blk1
-- ::47.378 GCD-test[:] blk2
-- ::47.378 GCD-test[:] blk0
-- ::47.378 GCD-test[:] done
如果dispatch_group_wait返回值不为0,就意味着虽然经过了指定的时间,但属于dispatch group的某一个处理还在进行中,你可以尝试将NSEC_PER_SEC改为NSEC_PER_USEC,这里不做详细介绍了。如果返回值为0,那么全部处理执行结束。如果等待时间为DISPATCH_TIME_FOREVER返回值恒为0,
在主线程的runloop的每次循环中,可检查执行是否结束,从而不耗费多余的等待时间,虽然这样也可以,但一般在这种形势下,推荐使用dispatch_group_notify追加结束处理到main_get_main_queue,因为这样可以简化代码。
(6)diapatch_barrier_async
在访问数据库文件时,如前所述,使用serial dispatch queue可避免数据竞争问题。写入处理确实不可与其他的写入处理以及包含读取处理的其它某些处理并行执行。但是如果读取处理只是与读取处理并行执行,那么多个并行执行就不会发生问题。dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行,该函数需要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用。
1.实现高效率的数据库访问和文件访问。
2.避免数据竞争。
代码示例:
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.ForBarrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"dispatch-1");
    });
    dispatch_async(queue, ^{
        NSLog(@"dispatch-2");
    });
    dispatch_barrier_async(queue, ^{
        sleep();
        NSLog(@"dispatch-barrier");
    });
    dispatch_async(queue, ^{
        NSLog(@"dispatch-3");
    });
    dispatch_async(queue, ^{
        NSLog(@"dispatch-4");
    });
-- ::12.270 GCD-test[:] dispatch-
-- ::12.270 GCD-test[:] dispatch-
-- ::17.275 GCD-test[:] dispatch-barrier
-- ::17.276 GCD-test[:] dispatch-
-- ::17.276 GCD-test[:] dispatch-
(7)dispatch_apply
会等待全部处理执行结束,可用于对nsarray对象的所有元素分别进行处理时,不必一个一个便携for循环部分。
(8)dispatch_sync
同步的将Block追加到指定的Dispatch Queue中。在追加的Block结束之前,Dispatch Queue函数会一直等待。
死锁:dispatch_sync的当前执行队列与提交block执行的目标队列相同时造成死锁。dispatch_sync会堵塞当前线程,等待block执行完毕后返回,但是当前的block加入到了当前的队列等待dispatch_sync的执行完毕,互相依赖,产生死锁。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
NSLog(@"hello");
});
iOS多线程——GCD的更多相关文章
- iOS多线程 GCD
		
iOS多线程 GCD Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main que ...
 - iOS 多线程GCD的基本使用
		
<iOS多线程简介>中提到:GCD中有2个核心概念:1.任务(执行什么操作)2.队列(用来存放任务) 那么多线程GCD的基本使用有哪些呢? 可以分以下多种情况: 1.异步函数 + 并发队列 ...
 - iOS 多线程 GCD part3:API
		
https://www.jianshu.com/p/072111f5889d 2017.03.05 22:54* 字数 1667 阅读 88评论 0喜欢 1 0. 预备知识 GCD对时间的描述有些新奇 ...
 - ios多线程-GCD基本用法
		
ios中多线程有三种,NSTread, NSOperation,GCD 这篇就讲讲GCD的基本用法 平时比较多使用和看到的是: dispatch_async(dispatch_get_global_q ...
 - iOS多线程——GCD与NSOperation总结
		
很长时间以来,我个人(可能还有很多同学),对多线程编程都存在一些误解.一个很明显的表现是,很多人有这样的看法: 新开一个线程,能提高速度,避免阻塞主线程 毕竟多线程嘛,几个线程一起跑任务,速度快,还不 ...
 - iOS多线程GCD的使用
		
1. GCD 简介 Grand Central Dispatch(GCD)是异步执行任务的技术之一.一般将应用程序中记述的线程管理用的代码在系统级中实现.开发者只需要定义想执行的任务并追加到适当的Di ...
 - iOS多线程GCD详解
		
在这之前,一直有个疑问就是:gcd的系统管理多线程的概念,如果你看到gcd管理多线程你肯定也有这样的疑问,就是:并发队列怎么回事,即是队列(先进先出)怎么会并发,本人郁闷了好久,才发现其实cgd管理多 ...
 - iOS多线程GCD的简单使用
		
在iOS开发中,苹果提供了三种多线程技术,分别是: (1)NSThread (2)NSOperation (3)GCD 简单介绍一下GCD的使用. GCD全称 Grand Central Dispat ...
 - iOS多线程——GCD篇
		
什么是GCD GCD是苹果对多线程编程做的一套新的抽象基于C语言层的API,结合Block简化了多线程的操作,使得我们对线程操作能够更加的安全高效. 在GCD出现之前Cocoa框架提供了NSObjec ...
 - iOS 多线程GCD简介
		
一.简介 1.1 GCD (Grand Central Dispatch )是Apple开发的一个多核编程的解决方法. Grand 含义是“伟大的.宏大的”,Central含义“中央的”,Dispat ...
 
随机推荐
- MyBatis绑定错误--BindingException:Invalid bound statement (not found)
			
如果出现: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found) 一般的原因是Mapper i ...
 - Qt入门之基础篇(1):Qt4及Qt5的下载与安装
			
转载请注明出处:CN_Simo. 导语: Qt是一个跨平台的C++图形界面应用程序框架.它提供给开发者建立图形用户界面所需的功能,广泛用于开发GUI程序,也可用于开发非GUI程序.Qt很容易扩展,并且 ...
 - intelliJ IDEA创建web工程
			
1.创建project,也就是eclipse里的workspace,eclipse里1个工作空间里可以创建多个工程,idea的一个工作空间里只创建1个工程. File -- New -- Projec ...
 - Java语言跨平台原理
			
Java语言有一个很重要的原理叫:跨平台性. 在介绍Java语言的跨平台性之前首先要介绍一个很重要的概念:JVM: JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一 ...
 - Jenkins的插件
			
Jenkins不仅自己为大家提供了很多功能,而且还支持插件.用户可以根据自己的需要安装插件,或者是自己开发插件. 这里说一下Jenkins的插件的安装方法: 首先,打开Jenkins首页,选择Jenk ...
 - Python抓取第一网贷中国网贷理财每日收益率指数
			
链接:http://www.p2p001.com/licai/index/id/147.html 所需获取数据链接类似于:http://www.p2p001.com/licai/shownews/id ...
 - block、inline、inline-block对比
			
display:block 1.block元素会独占一行,多个block元素会各种新起一行.默认情况下,block元素宽度自动填满其父元素容器: 2.block元素可以设置width和height属性 ...
 - Ext Js详解指南
			
什么是Ext JS 走进Ext的世界 Ext JS是一款富客户端开发框架它基于javascript.HTML和CSS开发而成,无需安装任何插件即可在常用浏览器中创建出绚丽的页面效果. 个人总结Ext ...
 - 一步一步在Windows中使用MyCat负载均衡 下篇
			
之前在 一步一步在Windows中使用MyCat负载均衡 上篇 中已经讲了如何配置出MyCat.下面讲其相关的使用. 五.配置MyCat-eye 对于MyCat监控官网还提供一个MyCat-eye w ...
 - ZwQueryVirtualMemory枚举进程模块
			
ZwQueryVirtualMemory算是枚举进程方法中的黑科技吧,主要是该方法可以检测出隐藏的模块(类似IceSword). 代码VS2015测试通过 再次奉上源码链接:https://githu ...