对GCD的一些理解和实践

对GCD的一些理解和实践

GCD

  GCD,全程Grand Central Dispatch,是苹果为了多核并行提出的解决方案。它是使用C语言实现,但是由于用了block来处理回调,所以使用起来十分方便。并且GCD会自动管理线程的生命周期,不需要我们去管理。

任务和队列

  GCD中有两个重要的概念,任务和队列。

  1、任务,就是我们想要处理的事情,任务可以分为同步执行和异步执行:

  同步(sync):使用dispatch_sync(dispatch_queue_t queue, dispatch_block_t block) 创建,同步执行任务时会阻塞当前线程等待block执行完毕返回。然后当前线程才会继续执行下去。

  异步(async):使用dispatch_async(dispatch_queue_t queue, dispatch_block_t block)创建,异步任务不会阻塞当前线程,任务创建后立即返回线程往下执行。

  2、队列,存放任务,并将任务由先入先出地派发出去。分为串行队列和并行队列:

  串行队列(Serial queue):队列中的任务根据创建顺序,先入先出地执行,等待上一个任务执行完毕后,才会执行下一个任务,有严格的执行先后顺序。

  并行队列(Concurrent queue):队列会根据先后顺序,将任务派发出去,并行执行。所有的任务几乎都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。有一个表,可以大体说明任务和队列之间的配合使用:

 串行队列并行队列
同步执行任务当前线程,一个一个执行当前线程,一个一个执行
异步执行任务另开线程,一个一个执行开很多线程,一起执行

  下面是任务和队列的使用演示:

  

复制代码
/***  串行队列中的任务会等待正在执行的任务执行结束,排队执行*/dispatch_queue_t serial_queue = dispatch_queue_create("serial.queue", DISPATCH_QUEUE_SERIAL);//主队列dispatch_queue_t mainQueue = dispatch_get_main_queue();/***  并行,不等待正在执行的任务的处理结果,可以并发执行多个任务*/dispatch_queue_t concurrent_queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);//全局队列dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//在串行队列中创建几个同步任务,在主线程顺序执行dispatch_sync(serial_queue, ^{NSLog(@"task a ,thread:%@",[NSThread currentThread]);});dispatch_sync(serial_queue, ^{NSLog(@"task b ,thread:%@",[NSThread currentThread]);});dispatch_sync(serial_queue, ^{NSLog(@"task c ,thread:%@",[NSThread currentThread]);});//在串行队列中创建几个异步任务,在主线程顺序执行dispatch_sync(serial_queue, ^{NSLog(@"task aa ,thread:%@",[NSThread currentThread]);});dispatch_sync(serial_queue, ^{NSLog(@"task bb ,thread:%@",[NSThread currentThread]);});dispatch_sync(serial_queue, ^{NSLog(@"task cc ,thread:%@",[NSThread currentThread]);});//在串行队列中创建几个异步任务,另开1个线程顺序执行dispatch_async(serial_queue, ^{NSLog(@"task 1 ,thread:%@",[NSThread currentThread]);});dispatch_async(serial_queue, ^{NSLog(@"task 1 ,thread:%@",[NSThread currentThread]);});dispatch_async(serial_queue, ^{NSLog(@"task 1 ,thread:%@",[NSThread currentThread]);});//在并队列中创建几个异步任务,另开多个线程同时执行dispatch_async(concurrent_queue, ^{NSLog(@"task 11 ,thread:%@",[NSThread currentThread]);});dispatch_async(concurrent_queue, ^{NSLog(@"task 22 ,thread:%@",[NSThread currentThread]);});dispatch_async(concurrent_queue, ^{NSLog(@"task 33 ,thread:%@",[NSThread currentThread]);});
复制代码

 

任务组和栅栏

  有时候当我们想要为多个任务添加依赖关系的时候,就可以使用任务组dispatch_group和dispatch_barrier。

  任务组是将若干个任务放在一个group 中,这些任务可以在同一队列也可以在不同队列,然后用dispatch_group_notify()和dispatch_group_wait()对任务组中任务的完成进行处理。

1、dispatch_group_notify()中的任务会在group中多有任务执行完毕后执行

复制代码
dispatch_group_t group = dispatch_group_create();/***  group中所有任务任务执行完毕后,执行dispatch_group_notify中的任务*/dispatch_group_async(group, serial_queue, ^{sleep(2);NSLog(@"serial_queue1");});dispatch_group_async(group, serial_queue, ^{sleep(2);NSLog(@"serial_queue2");});dispatch_group_async(group, concurrent_queue, ^{sleep(2);NSLog(@"concurrent_queue1");});dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"return main queue");});
复制代码

 

2、dispatch_group_wait()中可传入一个给定时间,如果在等待时间结束前group所有任务执行完毕则返回0,否则返回非0,这个函数是一个同步任务

复制代码
    /***  dispatch_group_wait给定一个时间,如果在等待时间结束前group所有任务执行完毕则返回0,否则返回非0,这个函数是一个同步任务*/dispatch_group_async(group, serial_queue, ^{sleep(3);NSLog(@"serial_queue1");});dispatch_group_async(group, serial_queue, ^{sleep(2);NSLog(@"serial_queue2");});dispatch_group_async(group, concurrent_queue, ^{sleep(3);NSLog(@"concurrent_queue1");});long i = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 6));NSLog(@"-- %ld --",i);dispatch_group_async(group, concurrent_queue, ^{NSLog(@"finish all");});
复制代码

 

3、可以使用dispatch_group_enter()和dispatch_group_leave()来添加任务组的任务,两者必须要成对出现,两个函数之间的代码便是要加入任务住的任务

复制代码
/***  使用dispatch_group_enter和dispatch_group_leave添加组任务,两者必须要成对出现*/dispatch_group_enter(group);sleep(2);NSLog(@"1");dispatch_group_leave(group);dispatch_group_enter(group);dispatch_async(concurrent_queue, ^{sleep(3);NSLog(@"2");dispatch_group_leave(group);});dispatch_group_enter(group);sleep(2);NSLog(@"3");dispatch_group_leave(group);long i = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 6));NSLog(@"-- %ld --",i);dispatch_group_async(group, concurrent_queue, ^{NSLog(@"finish all");});
复制代码

 

  栅栏是将一个队列中的任务分割成两部分,在栅栏任务之前添加的任务全部执行完毕后,单独执行栅栏任务,执行完毕后,再继续执行后面的任务。栅栏必须单独执行,不能与其他任务并发执行,因此,栅栏只对并发队列有意义。栅栏只有等待当前队列所有并发任务都执行完毕后,才会单独执行,待其执行完毕,再按照正常的方式继续向下执行。

复制代码
dispatch_queue_t concurrent_queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrent_queue, ^{sleep(2);NSLog(@"1");});dispatch_async(concurrent_queue, ^{sleep(2);NSLog(@"2");});dispatch_async(concurrent_queue, ^{sleep(2);NSLog(@"3");});dispatch_barrier_async(concurrent_queue, ^{sleep(2);NSLog(@"barrier");});dispatch_async(concurrent_queue, ^{sleep(2);NSLog(@"finish1");});dispatch_async(concurrent_queue, ^{NSLog(@"finish2");});
复制代码

 

重复执行dispatchApply 和 单次执行dispatch_once

  dispatch_apply()是将一个任务提交到队列中重复执行,并行或者串行由队列决定,dispatch_apply会阻塞当前线程,等到所有任务完成后返回

复制代码
dispatch_queue_t concurrent_queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_apply(5, concurrent_queue, ^(size_t index) {sleep(1);NSLog(@"index:%zu",index);});NSLog(@"finish");
复制代码

  dispatch_once()确保block内代码在整个应用运行期间只执行一次

//确保block内代码在整个应用运行期间只执行一次static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{NSLog(@"just run once in application");});

线程同步、信号量和线程死锁  

  多线程处理经常需要考虑到资源抢占的问题,比如经典的购票问题,写数据库等问题。处理问题的办法有很多,下面介绍最简单的两种做法:加上同步锁,或者用信号量来解决。

  同步锁,每当有线程访问锁里的资源时,会将此部分锁住,拒绝其他线程访问。直到占用的线程推出后才解锁,允许其他资源访问。

//同步锁,对block内的代码加锁,同一时间内只允许一个线程访问@synchronized(self) {NSLog(@"lock");};

  信号量dispatch_semaphore,一开始设置信号的总量,然后用dispatch_semaphore_wait()和dispatch_semaphore_signal()来管理信号量,达到控制线程访问的目的

复制代码
//信号量dispatch_semaphoredispatch_group_t group = dispatch_group_create();//设置总信号量dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);for (int i = 0; i<100; i++) {//设置等待信号,如果此时信号量大于0,那么信号量减一并继续往下执行//如果此时信号量小于0,会一直等待,直到超时//如果超时返回非零,成功执行返回0dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*50));dispatch_group_async(group, concurrent_queue, ^{sleep(1);NSLog(@"%d",i);//发送信号,让信号量加一dispatch_semaphore_signal(semaphore);});}dispatch_group_notify(group, concurrent_queue, ^{NSLog(@"finish");});
复制代码

  

  GCD尽管使用起来非常方便,但是如果使用不当也活造成一些麻烦,下面列举几个会造成线程死锁的场合:

复制代码
//在并行队列中,在当前队列调用dispatch_sync,并传入当前队列执行,并不会造成deadlock。dispatch_sync会阻塞当前线程,但是由于队列是并行执行,所以block中的任务会马上执行后返回。
- (void)syncAndConcurrentQueue {dispatch_queue_t queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_sync(queue, ^{NSLog(@"Jump to concurrent.queue! ,thread:%@",[NSThread currentThread]);dispatch_sync(queue, ^{sleep(3);NSLog(@"success6 ,thread:%@",[NSThread currentThread]);});NSLog(@"return");});
}//在串行队列中,在当前队列调用dispatch_sync,并传入当前队列执行,会造成deadlock。 dispatch_sync会阻塞当前线程,等待block中的任务执行完之后再继续执行,但是由于队列是串行执行,block中的任务放在最后,所以永远没有机会执行,线程死锁
- (void)synAndSerialQueue {dispatch_queue_t queue = dispatch_queue_create("serial.queue", DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{NSLog(@"Jump to serial.queue!");dispatch_sync(queue, ^{NSLog(@"success");});NSLog(@"return");});
}//任务1会阻塞主线程,直到block中执行完毕返回,任务二在主线程添加了了一个同步任务,阻塞当前线程,知道任务执行完毕返回,而任务2没有机会被执行。造成两条线程死锁。
- (void)recycle {dispatch_queue_t concurrent_queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);//任务1dispatch_sync(concurrent_queue, ^{NSLog(@"jump to concurrent queue");//任务2dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"return main queue");});});
}
复制代码

 

 

总结

  以上是使用GCD的一些心得,GCD使用起来尽管十分便利,但是在处理一些场景比如取消任务时候会很麻烦,所以实现简单功能的时候推荐使用GCD,如果功能复杂的话建议使用NSOperation和NSOperationQueue,NSOperationQueue的底层也是由GCD实现的,完全面向对象,所以使用起来更好理解。下次有空讲讲NSOperation和NSOperationQueue。以上代码demo地址:https://github.com/GarenChen/GCDDemo

 原本文链接:http://www.cnblogs.com/GarenChen/p/5858280.html

转载于:https://www.cnblogs.com/jx451578429/p/5893373.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/260518.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Buzz Today]2012.08.08

# Dark Reign 2 源代码现身Google Code Pandemic工作室开发的即时战略游戏《Dark Reign 2》源代码被泄露到了Google Code http://code.google.com/p/darkreign2/ # Warsow 1.0发布 Set in a futuristic cartoonish world, Warsow is a completely free fast-paced first-person…

PyTorch训练中Dataset多线程加载数据,比Dataloader里设置多个workers还要快

PyTorch训练中Dataset多线程加载数据&#xff0c;而不是在DataLoader 背景与需求 现在做深度学习的越来越多人都有用PyTorch&#xff0c;他容易上手&#xff0c;而且API相对TF友好的不要太多。今天就给大家带来最近PyTorch训练的一些小小的心得。 大家做机器学习、深度学习都…

python调用摄像头人脸识别代码_利用face_recognition,dlib与OpenCV调用摄像头进行人脸识别...

用已经搭建好 face_recognition&#xff0c;dlib 环境来进行人脸识别 未搭建好环境请参考&#xff1a; 使用opencv 调用摄像头 import face_recognition import cv2 video_capture cv2.videocapture(0) # videocapture打开摄像头&#xff0c;0为笔记本内置摄像头&#xff0c;1…

【转】彻底搞清计算结构体大小和数据对齐原则

数据对齐: 许多计算机系统对基本数据类型合法地址做出了一些限制&#xff0c;要求某种类型对象的地址必须是某个值K(通常是2&#xff0c;4或8)的倍数。这种对齐限制简化了形成处理器和存储器系统之间的接口的硬件设计。例如&#xff0c;假设一个处理器总是从存储器中取出8个字节…

Pytorch 学习率衰减 之 余弦退火与余弦warmup 自定义学习率衰减scheduler

学习率衰减&#xff0c;通常我们英文也叫做scheduler。本文学习率衰减自定义&#xff0c;通过2种方法实现自定义&#xff0c;一是利用lambda&#xff0c;另外一个是继承pytorch的lr_scheduler import math import matplotlib.pyplot as plt import numpy as np import torch i…

c++ 字符串赋给另一个_7.2 C++字符串处理函数

点击上方“C语言入门到精通”&#xff0c;选择置顶第一时间关注程序猿身边的故事作者闫小林白天搬砖&#xff0c;晚上做梦。我有故事&#xff0c;你有酒么&#xff1f;C字符串处理函数C语言和C提供了一些字符串函数&#xff0c;使得用户能很方便地对字符串进行处理。这些是放在…

如何检测远程主机上的某个端口是否开启

有时候我们要测试远程主机上的某个端口是否开启&#xff0c;无需使用太复杂的工作&#xff0c;windows下就自带了工具&#xff0c;那就是telnet。怎么检测呢&#xff0c;按下面的步骤&#xff1a; 1、安装telnet。我的win7下就没有telnet&#xff0c;在cmd下输入telnet提示没有…

Windows10 + WSL (Ubuntu) + Anaconda + vscode 手把手配置python运行环境(含虚拟环境)

配置WSL windows桌面下&#xff0c;按下面顺序可以找到 "启动或关闭windows功能” &#xff0c; 开始 -> 设置 -> 应用 -> 应用和功能 -> 可选功能 -> 相关设置下 更多Windows功能&#xff08;滚动鼠标到底部&#xff09;点击后&#xff0c;会弹出 启动或…

2019编译ffepeg vs_如何在windows10下使用vs2017编译最新版本的FFmpeg和ffplay

该文章描述了如何在windows10 64位系统下面编译出FFmpeg的库及其自带的ffplay播放器&#xff0c;而且全部采用最新的版本&#xff0c;这样我们可以在vs2017的ide下调试ffplay&#xff0c;能使我们更容易学习FFmpeg的架构以及音视频播放器的原理。步骤&#xff1a;1.安装vs2017在…

训练集山准确率高测试集上准确率很低_推荐算法改版前的AB测试

编辑导语&#xff1a;所谓推荐算法就是利用用户的一些行为&#xff0c;通过一些数学算法&#xff0c;推测出用户可能喜欢的东西&#xff1b;如今很多软件都有这样的操作&#xff0c;对于此系统的设计也会进行测试&#xff1b;本文作者分享了关于推荐算法改版前的AB测试&#xf…

C#实现渐变颜色的Windows窗体控件

C#实现渐变颜色的Windows窗体控件! 1,定义一个BaseFormGradient,继承于System.Windows.Forms.Form2,定义三个变量: privateColor _Color1 Color.Gainsboro; privateColor _Color2 Color.White; privatefloat_ColorAngle 0f;3,重载OnPaintBackground方法 protecte…

Windows下 jupyter notebook 运行multiprocessing 报错的问题与解决方法

文章目录测试用的代码错误解决方法测试用的代码 下面每一个对应一个jupyter notebook的单元格 import time from multiprocessing import Process, Queuedef generator():c 0while True:time.sleep(1.0) # read somethingyield cc 1%%timeds generator() for i in range(3…

vc mysql_vc6.0连接mysql数据库

一、MySQL的安装Mysql的安装去官网下载就可以。。。最新的是5.7版本。。二、VC6.0的设置(1)打开VC6.中选0 工具栏Tools菜单下的Options选项&#xff0c;在Directories的标签页中右边的“Show directories for:”下拉列表中“Includefiles”&#xff0c;然后在中间列表框中添加你…

python class用法_python原类、类的创建过程与方法

【小宅按】今天为大家介绍一下python中与class 相关的知识……获取对象的类名python是一门面向对象的语言&#xff0c;对于一切接对象的python来说&#xff0c;咱们有必要深入的学习与了解一些知识首先大家都知道&#xff0c;要获取一个对象所对应的类&#xff0c;需要使用clas…

深度学习中的一些常见的激活函数集合(含公式与导数的推导)sigmoid, relu, leaky relu, elu, numpy实现

文章目录Sigmoid(x)双曲正切线性整流函数 rectified linear unit &#xff08;ReLu&#xff09;PReLU(Parametric Rectified Linear Unit) Leaky ReLu指数线性单元 Exponential Linear Units &#xff08;ELU&#xff09;感知机激活%matplotlib inline %config InlineBackend.f…

最牛X的GCC 内联汇编

正如大家知道的&#xff0c;在C语言中插入汇编语言&#xff0c;其是Linux中使用的基本汇编程序语法。本文将讲解 GCC 提供的内联汇编特性的用途和用法。对于阅读这篇文章&#xff0c;这里只有两个前提要求&#xff0c;很明显&#xff0c;就是 x86 汇编语言和 C 语言的基本认识。…

mysql的告警日志_MySQL Aborted connection告警日志的分析

前言&#xff1a;有时候&#xff0c;连接MySQL的会话经常会异常退出&#xff0c;错误日志里会看到"Got an error reading communication packets"类型的告警。本篇文章我们一起来讨论下该错误可能的原因以及如何来规避。1.状态变量Aborted_clients和Aborted_connects…

hosts多个ip对应一个主机名_一个简单的Web应用程序,用作连接到ssh服务器的ssh客户端...

WebSSH一个简单的Web应用程序&#xff0c;用作连接到ssh服务器的ssh客户端。它是用Python编写的&#xff0c;基于tornado&#xff0c;paramiko和xterm.js。特征支持SSH密码验证&#xff0c;包括空密码。支持SSH公钥认证&#xff0c;包括DSA RSA ECDSA Ed25519密钥。支持加密密钥…

Shell Notes(1)

> vi复制粘贴 光标移动到要复制的部分的开头&#xff0c;Esc退出插入模式&#xff0c;按v进入Visual模式&#xff0c;用hjkl选中要复制的部分 按Y或者yy&#xff0c;复制 移动光标到目标位置&#xff0c;按p&#xff0c;粘贴 > echo –e 参数 –e 可以使echo解释由反斜杠…

mysql多表查询语句_mysql查询语句 和 多表关联查询 以及 子查询

1.查询一张表&#xff1a;select * from 表名&#xff1b;2.查询指定字段&#xff1a;select 字段1&#xff0c;字段2&#xff0c;字段3….from 表名&#xff1b;3.where条件查询&#xff1a;select字段1&#xff0c;字段2&#xff0c;字段3 frome表名 where 条件表达式&#x…