“少侠,你能左手画方,又手画圆吗?”bash
“这有何难,开始看我表演吧”多线程
“看,怎么样,厉害吧”异步
“鹅 .... 厉害...”async
“少侠,其实我是说你在iOS代码里能左手画方,右手画圆吗?”ui
“原来是这个意思,就是多线程嘛,这有何难,看我用GCD展现一下”spa
NSLog(@"左手画方");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"右手画圆");
});
//输出
左手画方
右手画圆
复制代码
“怎么样厉害吧”线程
“少侠确实有两下子,那对于GCD,少侠还有其余用法嘛”code
“啥?其余用法,不须要的,只须要如上我这等操做,就能解决大部分问题,其余用法,不须要的。”cdn
“非也、非也,GCD这本秘籍仍是有不少招式的,且听老夫给你细细道来。”blog
同步 画-完-告
小师妹让你画一个方,收到任务你开始画方,画完以后告知小师妹,方已完成。同步要阻塞当前线程,必需要等待当前任务执行完,返回之后,才会继续执行下一个任务。
异步 画-立-告
小师妹让你画一个方,收到任务后,为了显示你的厉害所在,你立马告知小师妹,方我已经着人去画啦,你且等着吧,很快就有消息了。异步不会阻塞当前线程,会当即返回,可马上执行下一个任务。
串行队列 一心一用
小师妹让你画一方一圆,收到任务,你的所有注意力放在一个地方,左手画方的时候,所有注意力在左手,因此此时右手是不动的,等待左手画完方以后,所有注意力集中右手,这时右手再画圆。此为先左手画方,后右手画圆。一心一用理解为串行,串行队列中的任务只能一次执行一个。
并行队列 一心两用
你的注意力分开两部分,一部分在左手,一部分在右手,而后左手画方,以后立马开始右手画圆,一心两用理解为并行。并行队列中的任务会按照加入队列中的顺序执行,但哪一个任务开始执行完是不定的。
“等等,大师,你的这些心法,貌似没什么用啊,个人招式仍是能解决大部门问题啊”
“那老夫就来问几个小问题,看招”
“多个线程同时读写一份数据,如何作到不出错?
多个异步任务都完成以后,才能告知小师妹,如何作到? ”
“鹅....”
“嘿嘿,不懂了吧,下面老夫就来给你展现GCD的八个基本招式,学会基本招式,这些问题就迎刃而解了”
dispatch_sync
NSLog(@"画方");
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"画圆");
});
NSLog(@"吹口哨")
//输出
画方
画圆
吹口哨
复制代码
dispatch_sync 会阻塞当前线程,等待block中的任务执行完毕后,才会执行下一个任务,因此执行顺序是 画方-画圆-吹口哨。
dispatch_async
NSLog(@"画方");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"画圆");
});
NSLog(@"吹口哨")
//输出
画方
吹口哨
画圆
复制代码
dispatch_async 不会阻塞当前线程,会当即执行下一个任务。因此执行顺序是 画方-吹口哨-画圆。
dispatch_after
指定时间后,将要执行的任务添加到指定的队列中去。
//两秒以后吹口哨
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"吹口哨");
});
复制代码
示例中2秒钟以后,将任务添加到主队列中,若是主队列中这时有其余的耗时操做,那么将会等待主队列中的任务执行完之后,才会执行添加的任务。 因此dispatch_after是不许确的。
dispatch_once
+ (instancetype)shareUserInfo
{
static UserInfo *shareUserInfo = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareUserInfo = [[UserInfo alloc] init];
});
return shareUserInfo;
}
复制代码
dispatch_once能够帮助咱们建立单例,不再用像之前同样费心费力啦。
dispatch_barrier
在使用dispatch_barrier的队列中,执行顺序以下:
//不使用dispatch_barrier
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(2);
NSLog(@"画方");
});
dispatch_async(queue, ^{
sleep(3);
NSLog(@"画圆");
});
NSLog(@"吹口哨");
dispatch_async(queue, ^{
NSLog(@"撩师妹");
});
dispatch_async(queue, ^{
NSLog(@"告师傅");
});
//输出
吹口哨
撩师妹
告师傅
画方
画圆
复制代码
由于画方画圆的时间过久,致使了活儿还没干,就先吹口哨了,这是不对的。使用dispatch_barrier再试一次。
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(2);
NSLog(@"画方");
});
dispatch_async(queue, ^{
sleep(3);
NSLog(@"画圆");
});
dispatch_barrier_sync(queue, ^{
sleep(4);
NSLog(@"吹口哨");
});
dispatch_async(queue, ^{
NSLog(@"撩师妹");
});
dispatch_async(queue, ^{
NSLog(@"告师傅");
});
//输出
画方
画圆
吹口哨
撩师妹
告师傅
复制代码
能够看到虽然画方画圆的时间比较久,吹口哨由于技术不成熟,也耗时较久,但最终是按照正常流程进行,活儿没干完坚定不撩师妹。
清楚了吧,吹口哨的时候,其余的都靠边站,就是这么横。
“大师,为何图中是并行队列,而不是串行队列?”
“由于串行队列原本就是按顺序执行的...”
dispatch_group
画方-画圆-吹口哨-撩师妹-告师傅,流程行云流水。 如今新的问题来了,那想在画方-画圆以后,喝口水再吹口哨要怎么作呢?
首先来分析一下问题:
要达到上面的要求,一个比较普通的流程是画方完成后用一个变量记录,画圆完成后用另一个变量记录。当两个变量都记录到完成以后,就能够继续下一步了,喝水。
麻烦吗? 也不麻烦,才两个变量而已。可是上面的耗时操做若是是5个呢? 麻烦! dispatch_group 能够方便的解决这个问题。
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
sleep(3);
NSLog(@"画方完成");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
sleep(2);
NSLog(@"画圆完成");
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"都完成了 喝口水");
});
//输出
画圆完成
画方完成
都完成了 喝口水
复制代码
少侠你看,前面提到的问题之一:“多个异步任务都完成以后,才能告知小师妹,如何作到?” 就这样解决了。
有一点要注意,dispatch__group__enter 和 dispatch__group__leave必须成对出现,否则会崩溃的。
dispatch_semaphore
信号量能够控制多个线程对有限资源的访问。
dispatch_semaphore只有三个方法:
//建立信号量
dispatch_semaphore_create
//发送信号量
dispatch_semaphore_signal
//等待信号量
dispatch_semaphore_wait
复制代码
以少侠为例:
少侠如今要去马场,给你的汗血宝马喂草,可是马场的马槽是有限的,其余的人也要喂马,少侠纵然有绝世武功,但人在江湖,依然要讲究江湖规矩——排队。
dispatch__semaphore__create 建立时,传入的数值,为马槽数量。
dispatch__semaphore__signal 至关于走了一匹马。
dispatch__semaphore__wait 至关于来了一匹马。
调用一次 dispatch__semaphore__signal 就走了一匹马,调用一次 dispatch__semaphore__wait 就来了一匹马,当马槽的数量为0时,再来的马就只能等待了。若是有耐心能够一直等,没有耐心,等半个时辰也能够走。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"准备画方");
sleep(2);
NSLog(@"画方");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"准备画圆");
sleep(2);
NSLog(@"画圆");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"准备吹口哨");
sleep(2);
NSLog(@"吹口哨");
dispatch_semaphore_signal(semaphore);
});
//输出
准备吹口哨
准备画方
准备画圆
画圆
吹口哨
画方
复制代码
建立信号量值为3,表明最多能够三处资源同时访问,当前有3个线程,因此当前没有资源控制,能够到看三个任务同时准备同时完成。
下面把信号量的值改成2
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"准备画方");
sleep(2);
NSLog(@"画方");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"准备画圆");
sleep(2);
NSLog(@"画圆");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"准备吹口哨");
sleep(2);
NSLog(@"吹口哨");
dispatch_semaphore_signal(semaphore);
});
//输出
准备画圆
准备画方
画圆
画方
准备吹口哨
吹口哨
复制代码
建立信号量值为2,表明最多能够两处资源同时访问。但当前有三个线程,因此前面两个任务 画圆、画方会先完成,以后再吹口哨。
少侠,到目前为止,老夫已经给你讲了GCD的四个基本心法,七个基本招式,这些都只是基本功,少侠往后必当好好练习,才能像更高的层次冲击。老夫累了,这就闭关了,他日你练成神功,老夫定当出关,助你再占高峰。