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. TFS独占签出代码

    最近发现微软给我们提供了免费的TFS,地址:http://tfs.visualstudio.com/, 就注册了一个,但是我发现没办法独占签出. 在公司里,TFS有服务端,所以很好设置,但是注册微软的 ...

  2. 蓝桥杯 2014本科C++ B组 六角填数 枚举排列

    标题:六角填数 如图[1.png]所示六角形中,填入1~12的数字. 使得每条直线上的数字之和都相同. 图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少? 请通过浏览器提交答案,不要填 ...

  3. bzoj5102

    $贪心$ $按左端点排序.$ $当我们钦定了最右的左端点,那么自然希望右端点尽量靠右$ $考虑之前的区间,那么我们相当于选之前的区间中第k大的右端点$ $堆维护一下就可以了,每次把新的元素放进堆,如果 ...

  4. idea创建vue项目,Terminal安装npm的淘宝镜像:'npm' 不是内部或外部命令,也不是可运行的程序 或批处理文件。

    原因: 安装node.js时,不是默认路径安装,环境变量找不到npm,需要改环境变量配置: 原下: 找到安装node.js的安装路径: 改后: 成功: npm i -g cnpm --registry ...

  5. docker的安装与卸载

    卸载老版本docker sudo apt-get remove docker docker-engine docker.io /var/lib/docker/目录下存放着 images, contai ...

  6. 2.11-2.12 HBase的数据迁移常见方式

    一.importtsv 把hdfs中数据抽取到HBase表中: 1.准备数据 ##student.tsv [root@hadoop-senior datas]# cat student.tsv 100 ...

  7. C++类静态变量的一种使用特例

    不同进程里的数据默认情况下是互不影响的. 静态变量是属于类本身的,它的所有实例可以共享这个静态变量,但是有个先天条件就是在同一个进程的情况下!!

  8. hexo添加新菜单并实现新菜单的文章归类

    1.添加收藏夹菜单,新建一个页面,命名为 favorite,命令如下: hexo new page favorite ## 然后就可以看到在source下多了一个favorite的文件夹,里面有一个i ...

  9. qscoj53(图的m着色问题)

    题目链接:http://qscoj.cn/contest/12/problem/53/ 题意:中文题诶- 思路:n个点, 那么最多用n种颜色,所以我们可以枚举颜色种类1~n,然后再判断用 i 种颜色可 ...

  10. [Xcode 实际操作]二、视图与手势-(12)UITapGestureRecognizer手势之双击

    目录:[Swift]Xcode实际操作 本文将演示使用视图的双击手势,完成视图的交互功能. import UIKit class ViewController: UIViewController { ...