GCD提供的一些操作队列的方法

名称 说明
dispatch_set_target_queue 将多个队列添加到目标队列中
dispatch_group 将多个队列放入组中,监听所有任务完成状
dispatch_suspend 队列挂起
dispatch_resume 队列恢复
dispatch_sync 线程同步执行
dispatch_async 线程异步执行
dispatch_after 延迟执行态
dispatch_barrier_async 并发队列中,完成在它之前提交到队列中的任务后打断后面任务
dispatch_apply 实现无序查找
  • dispatch_set_target_queue

系统的Global Queue是可以指定优先级的,那我们如何给自己创建的队列执行优先级呢?

这里我们就可以用到dispatch_set_target_queue这个方法:

dispatch_queue_t serialDiapatchQueue=dispatch_queue_create("com.test.queue", NULL);
dispatch_queue_t dispatchgetglobalqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, );
//将serialDiapatchQueue放到全局队列中作为子队列,这样优先级就是使用默认
dispatch_set_target_queue(serialDiapatchQueue, dispatchgetglobalqueue); dispatch_async(serialDiapatchQueue, ^{
  NSLog(@"我优先级低,先让让");
}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
  NSLog(@"我优先级高,我先block");
});

我把自己创建的队列塞到了系统提供的global_queue队列中,我们可以理解为:我们自己创建的queue其实是位于global_queue中执行,

所以改变global_queue的优先级,也就改变了我们自己所创建的queue的优先级。所以我们常用这种方式来管理子队列。

(一),使用dispatch_set_target_queue更改Dispatch Queue的执行优先级

dispatch_queue_create函数生成的DisPatch Queue不管是Serial DisPatch Queue还是Concurrent Dispatch Queue,执行的优先级都与默认优先级的Global Dispatch queue相同,如果需要变更生成的Dispatch Queue的执行优先级则需要使用dispatch_set_target_queue函数

- (void)testTeagerQueue1 {
  dispatch_queue_t serialQueue = dispatch_queue_create("com.oukavip.www",NULL);
  dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,);
// 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。
  dispatch_set_target_queue(serialQueue, globalQueue);
}

(二),使用dispatch_set_target_queue修改用户队列的目标队列,使多个serial queue在目标queue上一次只有一个执行

首先,我们需要阐述一下生成多个Serial DisPatch Queue时的注意事项

Serial DisPatch Queue是一个串行队列,只能同时执行1个追加处理(即任务),当用Dispatch_queue_create函数生成多个Serial DisPatch Queue时,每个Serial DisPatch Queue均获得一个线程,即多个Serial DisPatch Queue可并发执行,同时处理添加到各个Serial DisPatch Queue中的任务,但要注意如果过多地使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能,所以我们只在为了避免多个线程更新相同资源导致数据竞争时,使用Serial DisPatch Queue

第一种情况:使用dispatch_set_target_queue(Dispatch Queue1, Dispatch Queue2)实现队列的动态调度管理

- (void)testTargetQueue2 {
  //创建一个串行队列queue1
  dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
  //创建一个串行队列queue2
  dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);   //使用dispatch_set_target_queue()实现队列的动态调度管理
  dispatch_set_target_queue(queue1, queue2);   /*
  <*>dispatch_set_target_queue(Dispatch Queue1, Dispatch Queue2);
  那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行:   <*>dispatch_suspend(dispatchA);
  这时则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。   这里只简单举个例子,说明dispatch队列运行的灵活性,在实际应用中你会逐步发掘出它的潜力。   dispatch队列不支持cancel(取消),没有实现dispatch_cancel()函数,不像NSOperationQueue,不得不说这是个小小的
  */
  dispatch_async(queue1, ^{
    for (NSInteger i = ; i < ; i++) {
      NSLog(@"queue1:%@, %ld", [NSThread currentThread], i);
      [NSThread sleepForTimeInterval:0.5];
      if (i == ) {
        dispatch_suspend(queue2);
      }  
    }
  });   dispatch_async(queue1, ^{
    for (NSInteger i = ; i < ; i++) {
      NSLog(@"queue1:%@, %ld", [NSThread currentThread], i);
    }
  });   dispatch_async(queue2, ^{
    for (NSInteger i = ; i < ; i++) {
      NSLog(@"queue2:%@, %ld", [NSThread currentThread], i);
    }
  });
}

第二种情况:使用dispatch_set_target_queue将多个串行的queue指定到了同一目标,那么着多个串行queue在目标queue上就是同步执行的,不再是并行执行。

- (void)testTargetQueue {
//1.创建目标队列
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL); //2.创建3个串行队列
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); //3.将3个串行队列分别添加到目标队列
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue); 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");
});
}
  • dispatch_after

这个是最常用的,用来延迟执行的GCD方法,因为在主线程中我们不能用sleep来延迟方法的调用,所以用它是最合适的,我们做一个简单的例子:

NSLog(@"小破孩-波波1");
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(@"小破孩-波波2");
});

输出的结果:

-- ::06.019 GCD[:] 小破孩-波波1
-- ::08.019 GCD[:] 小破孩-波波2

我们看到他就是在主线程,就是刚好延迟了2秒,当然,我说这个2秒并不是绝对的,为什么这么说?还记得我之前在介绍dispatch_async这个特性的时候提到的吗?他的block中方法的执行会放在主线程runloop之后,所以,如果此时runloop周期较长的时候,可能会有一些时差产生。

  • dispatch_group

当我们需要监听一个并发队列中,所有任务都完成了,就可以用到这个group,因为并发队列你并不知道哪一个是最后执行的,所以以单独一个任务是无法监听到这个点的,如果把这些单任务都放到同一个group,那么,我们就能通过dispatch_group_notify方法知道什么时候这些任务全部执行完成了。

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(@"");});
dispatch_group_async(group, queue, ^{NSLog(@"");});
dispatch_group_async(group, queue, ^{NSLog(@"");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"down");
});

在例子中,我把3个log分别放在并发队列中,通过把这个并发队列任务统一加入group中,group每次runloop的时候都会调用一个方法dispatch_group_wait(group, DISPATCH_TIME_NOW),用来检查group中的任务是否已经完成,如果已经完成了,那么会执行dispatch_group_notify的block,输出’down’看一下运行结果:

 -- ::58.647 GCD[:]
-- ::58.647 GCD[:]
-- ::58.647 GCD[:]
-- ::58.650 GCD[:] down
  • dispatch_barrier_async

此方法的作用是在并发队列中,完成在它之前提交到队列中的任务后打断,单独执行其block,并在执行完成之后才能继续执行在他之后提交到队列中的任务:

dispatch_queue_t concurrentDiapatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});
dispatch_barrier_async(concurrentDiapatchQueue, ^{
sleep();
NSLog(@"");
});
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});
dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"");});

输出的结果为:

-- ::32.410 GCD[:]
-- ::32.410 GCD[:]
-- ::32.410 GCD[:]
-- ::32.410 GCD[:]
-- ::33.414 GCD[:]
-- ::33.415 GCD[:]
-- ::33.415 GCD[:]
-- ::33.415 GCD[:]
-- ::33.415 GCD[:]

4之后的任务在我线程sleep之后才执行,这其实就起到了一个线程锁的作用,在多个线程同时操作一个对象的时候,读可以放在并发进行,当写的时候,我们就可以用dispatch_barrier_async方法,效果杠杠的。

  • dispatch_sync

dispatch_sync 会在当前线程执行队列,并且阻塞当前线程中之后运行的代码,所以,同步线程非常有可能导致死锁现象,我们这边就举一个死锁的例子,直接在主线程调用以下代码:

dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"有没有同步主线程?");
});

*根据FIFO(先进先出)的原则,block里面的代码应该在主线程此次runloop后执行,但是由于他是同步队列,所有他之后的代码会等待其执行完成后才能继续执行,2者相互等待,所以就出现了死锁。
我们再举一个比较特殊的例子:

dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_sync(queue, ^{sleep();NSLog(@"");});
dispatch_sync(queue, ^{sleep();NSLog(@"");});
dispatch_sync(queue, ^{sleep();NSLog(@"");});
NSLog(@"");

其打印结果为:

 -- ::48.124 GCD[:]
-- ::49.125 GCD[:]
-- ::50.126 GCD[:]
-- ::50.126 GCD[:]

从线程编号中我们发现,同步方法没有去开新的线程,而是在当前线程中执行队列,会有人问,上文说dispatch_get_global_queue不是并发队列,并发队列不是应该会在开启多个线程吗?这个前提是用异步方法。GCD其实是弱化了线程的管理,强化了队列管理,这使我们理解变得比较形象

  • dispatch_apply

这个方法用于无序查找,在一个数组中,我们能开启多个线程来查找所需要的值,我这边也举个例子:

NSArray *array=[[NSArray alloc]initWithObjects:@"",@"",@"",@"",@"",@"",@"", nil];
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_apply([array count], queue, ^(size_t index) {
NSLog(@"%zu=%@",index,[array objectAtIndex:index]);
});
NSLog(@"阻塞");

输出结果

-- ::50.726 GCD[:] =
-- ::50.726 GCD[:] =
-- ::50.726 GCD[:] =
-- ::50.726 GCD[:] =
-- ::50.726 GCD[:] =
-- ::50.726 GCD[:] =
-- ::50.726 GCD[:] =
-- ::50.727 GCD[:] 阻塞

通过输出log,我们发现这个方法虽然会开启多个线程来遍历这个数组,但是在遍历完成之前会阻塞主线程。

  • dispatch_suspend & dispatch_resume

队列挂起和恢复,这个没什么好说的,直接上代码:

dispatch_queue_t concurrentDiapatchQueue=dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentDiapatchQueue, ^{
for (int i=; i<; i++)
{
NSLog(@"%i",i);
if (i==)
{
NSLog(@"-----------------------------------");
dispatch_suspend(concurrentDiapatchQueue);
sleep();
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_resume(concurrentDiapatchQueue);
});
}
}
});

我们甚至可以在不同的线程对这个队列进行挂起和恢复,因为GCD是对队列的管理。

  • Semaphore

我们可以通过设置信号量的大小,来解决并发过多导致资源吃紧的情况,以单核CPU做并发为例,一个CPU永远只能干一件事情,那如何同时处理多个事件呢,聪明的内核工程师让CPU干第一件事情,一定时间后停下来,存取进度,干第二件事情以此类推,所以如果开启非常多的线程,单核CPU会变得非常吃力,即使多核CPU,核心数也是有限的,所以合理分配线程,变得至关重要,那么如何发挥多核CPU的性能呢?如果让一个核心模拟传很多线程,经常干一半放下干另一件事情,那效率也会变低,所以我们要合理安排,将单一任务或者一组相关任务并发至全局队列中运算或者将多个不相关的任务或者关联不紧密的任务并发至用户队列中运算,所以用好信号量,合理分配CPU资源,程序也能得到优化,当日常使用中,信号量也许我们只起到了一个计数的作用,真的有点大材小用。

dispatch_semaphore_t semaphore = dispatch_semaphore_create();//为了让一次输出10个,初始信号量为10
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
for (int i = ; i <; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//每进来1次,信号量-1;进来10次后就一直hold住,直到信号量大于0;
dispatch_async(queue, ^{
NSLog(@"%i",i);
sleep();
dispatch_semaphore_signal(semaphore);//由于这里只是log,所以处理速度非常快,我就模拟2秒后信号量+1;
});
}
  • dispatch_once

这个函数一般是用来做一个单例,也是非常常用的,下面是一个简单用例:

static SingletonTimer * instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[SingletonTimer alloc] init];
}); return instance;

GCD 使用说明的更多相关文章

  1. 学习:数学----gcd及扩展gcd

    gcd及扩展gcd可以用来求两个数的最大公因数,扩展gcd甚至可以用来求一次不定方程ax+by=c的解   辗转相除法与gcd 假设有两个数a与b,现在要求a与b的最大公因数,我们可以设 a=b*q+ ...

  2. Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用

    OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...

  3. iOS 多线程之GCD的使用

    在iOS开发中,遇到耗时操作,我们经常用到多线程技术.Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法,只需定义想要执行的任务,然后添加到适当的调度队列 ...

  4. Atitit.项目修改补丁打包工具 使用说明

    Atitit.项目修改补丁打包工具 使用说明 1.1. 打包工具已经在群里面.打包工具.bat1 1.2. 使用方法:放在项目主目录下,执行即可1 1.3. 打包工具的原理以及要打包的项目列表1 1. ...

  5. 【swift】BlockOperation和GCD实用代码块

    //BlockOperation // // ViewController.swift import UIKit class ViewController: UIViewController { @I ...

  6. 修改版: 小伙,多线程(GCD)看我就够了,骗你没好处!

    多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系 ...

  7. awk使用说明

    原文地址:http://www.cnblogs.com/verrion/p/awk_usage.html Awk使用说明 运维必须掌握的三剑客工具:grep(文件内容过滤器),sed(数据流处理器), ...

  8. GCD的相关函数使用

    GCD 是iOS多线程实现方案之一,非常常用 英文翻译过来就是伟大的中枢调度器,也有人戏称为是牛逼的中枢调度器 是苹果公司为多核的并行运算提出的解决方案 1.一次性函数 dispatch_once 顾 ...

  9. hdu1695 GCD(莫比乌斯反演)

    题意:求(1,b)区间和(1,d)区间里面gcd(x, y) = k的数的对数(1<=x<=b , 1<= y <= d). 知识点: 莫比乌斯反演/*12*/ 线性筛求莫比乌 ...

随机推荐

  1. hihoCoser(#1149 : 回文字符序列)

    时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定字符串,求它的回文子序列个数.回文子序列反转字符顺序后仍然与原序列相同.例如字符串aba中,回文子序列为"a& ...

  2. atof,atoi,atol,strtod,strtol,strtoul

    字符串处理函数 atof 将字串转换成浮点型数 atoi 字符串转换成整型数 atol 函数名: atol 功 能: 把字符串转换成长整型数 用 法: long atol(const char *np ...

  3. python zlib字符串压缩

    在做网络程序时,可以对字符串进行压缩来节省带宽 项目中用到 {"compress": <压缩标记>, "result":[[设备类型.设备ID, 设 ...

  4. 安装openstack出现的问题及解决

    感谢http://www.cnblogs.com/nmap/p/6417163.html,参考这篇文章,我在虚拟机上部署成功了,后来因为虚拟机实在带不动,所以改装到物理机上,在实验室找到两台物理机,分 ...

  5. Ipython使用总结1

    安装了Anaconda就会发现安装了很多组件.也就省去了安装包时候的依赖问题 https://www.continuum.io/downloads 2 Ipython基础 (1)启动: win+R 启 ...

  6. Oracle 11g client 安装

    本文所有红色字体标注的为本人计算机安装方法(Oracle 11g安装在本地 Oracle 11g client 也是安装在本地 如果情况一致 可参照本人方法安装) Oracle 11g client ...

  7. 宝塔Linux 8888 进不去

    一.前言 导致该问题的原因是 Python 版本问题,可能是您更新了 python 的问题.参考宝塔问题的解决方案做的小结.仅供自己做笔记,不作其他用途. 二.解决方案 1.进入shell 命令行,输 ...

  8. [WIP]用已有db进行rails开发

    创建: 2019/01/16 晚点补上 https://qiita.com/edo1z/items/a0bf22b294406f00ec7c https://qiita.com/kentosasa/i ...

  9. 项目中常用的js骚操作

    //打开网址window.open("http://www.runoob.com"); //判断是否为url var url = $("#url").val() ...

  10. hihocoder #1335 : Email Merge(map+sort)

    传送门 题意 分析 每次插入人名与邮箱的时候,做一次并查集,然后做一次sort即可 trick 3 a 1 first@hihocoder.com b 1 second@hihocoder.com c ...