gcd什么情况用串行口和并行口什么情况用并行

&&国之画&&&&&&
版权所有 京ICP备号-2
迷上了代码!您所在的位置: &
GCD实战二:资源竞争
GCD实战二:资源竞争
dreamingwish
GCD是个比较范特西的技术,可以办到很多事儿,但是它不能为你办所有的事儿。所以,对于进行IO操作并且可能会使用大量内存的任务,我们必须仔细斟酌。当然,即使这样,GCD还是为我们提供了简单有效的方法来进行并发计算。
我将分四步来带大家研究研究程序的并发计算。第一步是基本的串行程序,然后使用GCD把它并行计算化。如果你想顺着步骤来尝试这些程序的话,可以下载源码。注意,别运行imagegcd2.m,这是个反面教材。
源码下载:
我们的程序只是简单地遍历~/Pictures然后生成缩略图。这个程序是个命令行程序,没有图形界面(尽管是使用Cocoa开发库的),主函数如下:
int&main(int&argc,&char&**argv)&{&&&&&NSAutoreleasePool&*outerPool&=&[NSAutoreleasePool&new];&&&&&NSApplicationLoad();&&&&&NSString&*destination&=&@&/tmp/imagegcd&;&&&&&[[NSFileManager&defaultManager]&removeItemAtPath:&destination&error:&NULL];&&&&&[[NSFileManager&defaultManager]&createDirectoryAtPath:&destination&IntermediateDirectories:&YES&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&attributes:&nil&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&error:&NULL];&&&&&Start();&&&&&NSString&*dir&=&[@&~/Pictures&&stringByExpandingTildeInPath];&&&&&NSDirectoryEnumerator&*enumerator&=&[[NSFileManager&defaultManager]&enumeratorAtPath:&dir];&&&&&int&count&=&0;&&&&&for(NSString&*path&in&enumerator)&&&&&{&&&&&&&&&NSAutoreleasePool&*innerPool&=&[NSAutoreleasePool&new];&&&&&&&&&if([[[path&pathExtension]&lowercaseString]&isEqual:&@&jpg&])&&&&&&&&&{&&&&&&&&&&&&&path&=&[dir&stringByAppendingPathComponent:&path];&&&&&&&&&&&&&&&&&&&&&&&&&&NSData&*data&=&[NSData&dataWithContentsOfFile:&path];&&&&&&&&&&&&&if(data)&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&NSData&*thumbnailData&=&ThumbnailDataForData(data);&&&&&&&&&&&&&&&&&if(thumbnailData)&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailName&=&[NSString&stringWithFormat:&@&%d.jpg&,&count++];&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailPath&=&[destination&stringByAppendingPathComponent:&thumbnailName];&&&&&&&&&&&&&&&&&&&&&[thumbnailData&writeToFile:&thumbnailPath&atomically:&NO];&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&}&&&&&&&&&[innerPool&release];&&&&&}&&&&&End();&&&&&[outerPool&release];&}&
如果你要看到所有的副主函数的话,到文章顶部下载源代码吧。当前这个程序是imagegcd1.m。程序中重要的部分都在这里了。.&Start&函数和&End&函数只是简单的计时函数(内部实现是使用的gettimeofday函数)。ThumbnailDataForData函数使用Cocoa库来加载图片数据生成Image对象,然后将图片缩小到320&320大小,最后将其编码为JPEG格式。
简单而天真的并发
乍一看,我们感觉将这个程序并发计算化,很容易。循环中的每个迭代器都可以放入GCD global queue中。我们可以使用dispatch
queue来等待它们完成。为了保证每次迭代都会得到唯一的文件名数字,我们使用OSAtomicIncrement32来原子操作级别的增加count数:
&&&&dispatch_queue_t&globalQueue&=&dispatch_get_global_queue(0,&0);&&&&&dispatch_group_t&group&=&dispatch_group_create();&&&&&__block&uint32_t&count&=&-1;&&&&&for(NSString&*path&in&enumerator)&&&&&{&&&&&&&&&dispatch_group_async(group,&globalQueue,&BlockWithAutoreleasePool(^{&&&&&&&&&&&&&if([[[path&pathExtension]&lowercaseString]&isEqual:&@&jpg&])&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&NSString&*fullPath&=&[dir&stringByAppendingPathComponent:&path];&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&NSData&*data&=&[NSData&dataWithContentsOfFile:&fullPath];&&&&&&&&&&&&&&&&&if(data)&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&NSData&*thumbnailData&=&ThumbnailDataForData(data);&&&&&&&&&&&&&&&&&&&&&if(thumbnailData)&&&&&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailName&=&[NSString&stringWithFormat:&@&%d.jpg&,&OSAtomicIncrement32(&)];&&&&&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailPath&=&[destination&stringByAppendingPathComponent:&thumbnailName];&&&&&&&&&&&&&&&&&&&&&&&&&[thumbnailData&writeToFile:&thumbnailPath&atomically:&NO];&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&});&&&&&}&&&&&dispatch_group_wait(group,&DISPATCH_TIME_FOREVER);&
这个就是imagegcd2.m,但是,注意,别运行这个程序,有很大的问题。&
如果你无视我的警告还是运行这个imagegcd2.m了,你现在很有可能是在重启了电脑后,又打开了我的页面。。如果你乖乖地没有运行这个程序的话,运行这个程序发生的情况就是(如果你有很多很多图片在~/Pictures中):电脑没反应,好久好久都不动,假死了。。
问题出在哪?就在于GCD的智能上。GCD将任务放到全局线程池中运行,这个线程池的大小根据系统负载来随时改变。例如,我的电脑有四核,所以如果我使用GCD加载任务,GCD会为我每个cpu核创建一个线程,也就是四个线程。如果电脑上其他任务需要进行的话,GCD会减少线程数来使其他任务得以占用cpu资源来完成。
但是,GCD也可以增加活动线程数。它会在其他某个线程阻塞时增加活动线程数。假设现在有四个线程正在运行,突然某个线程要做一个操作,比如,读文件,这个线程就会等待磁盘响应,此时cpu核心会处于未充分利用的状态。这是GCD就会发现这个状态,然后创建另一个线程来填补这个资源浪费空缺。
现在,想想上面的程序发生了啥?主线程非常迅速地将任务不断放入global
queue中。GCD以一个少量工作线程的状态开始,然后开始执行任务。这些任务执行了一些很轻量的工作后,就开始等待磁盘资源,慢得不像话的磁盘资源。
我们别忘记磁盘资源的特性,除非你使用的是SSD或者牛逼的RAID,否则磁盘资源会在竞争的时候变得异常的慢。。
刚开始的四个任务很轻松地就同时访问到了磁盘资源,然后开始等待磁盘资源返回。这时GCD发现CPU开始空闲了,它继续增加工作线程。然后,这些线程执行更多的磁盘读取任务,然后GCD再创建更多的工资线程。。。
可能在某个时间文件读取任务有完成的了。现在,线程池中可不止有四个线程,相反,有成百上千个。。。GCD又会尝试将工作线程减少(太多使用CPU资源的线程),但是减少线程是由条件的,GCD不可以将一个正在执行任务的线程杀掉,并且也不能将这样的任务暂停。它必须等待这个任务完成。所有这些情况都导致GCD无法减少工作线程数。
然后所有这上百个线程开始一个个完成了他们的磁盘读取工作。它们开始竞争CPU资源,当然CPU在处理竞争上比磁盘先进多了。问题在于,这些线程读完文件后开始编码这些图片,如果你有很多很多图片,那么你的内存将开始爆仓。。然后内存耗尽咋办?虚拟内存啊,虚拟内存是啥,磁盘资源啊。Oh
然后进入了一个恶性循环,磁盘资源竞争导致更多的线程被创建,这些线程导致更多的内存使用,然后内存爆仓导致虚拟内存交换,直至GCD创建了系统规定的线程数上限(可能是512个),而这些线程又没法被杀掉或暂停。。。
这就是使用GCD时,要注意的。GCD能智能地根据CPU情况来调整工作线程数,但是它却无法监视其他类型的资源状况。如果你的任务牵涉大量IO或者其他会导致线程block的东西,你需要把握好这个问题。
问题的根源来自于磁盘IO,然后导致恶性循环。解决了磁盘资源碰撞,就解决了这个问题。
GCD的custom queue使得这个问题易于解决。Custom queue是串行的。如果我们创建一个custom
queue然后将所有的文件读写任务放入这个队列,磁盘资源的同时访问数会大大降低,资源访问碰撞就避免了。
虾米是我们修正后的代码,使用IO queue(也就是我们创建的custom queue专门用来读写磁盘):
dispatch_queue_t&globalQueue&=&dispatch_get_global_queue(0,&0);&dispatch_queue_t&ioQueue&=&dispatch_queue_create(&com.mikeash.imagegcd.io&,&NULL);&dispatch_group_t&group&=&dispatch_group_create();&__block&uint32_t&count&=&-1;&for(NSString&*path&in&enumerator)&{&&&&&if([[[path&pathExtension]&lowercaseString]&isEqual:&@&jpg&])&&&&&{&&&&&&&&&NSString&*fullPath&=&[dir&stringByAppendingPathComponent:&path];&&&&&&&&&&&&&&&&&&dispatch_group_async(group,&ioQueue,&BlockWithAutoreleasePool(^{&&&&&&&&&&&&&NSData&*data&=&[NSData&dataWithContentsOfFile:&fullPath];&&&&&&&&&&&&&if(data)&&&&&&&&&&&&&&&&&dispatch_group_async(group,&globalQueue,&BlockWithAutoreleasePool(^{&&&&&&&&&&&&&&&&&&&&&NSData&*thumbnailData&=&ThumbnailDataForData(data);&&&&&&&&&&&&&&&&&&&&&if(thumbnailData)&&&&&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailName&=&[NSString&stringWithFormat:&@&%d.jpg&,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&OSAtomicIncrement32(&)];&&&&&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailPath&=&[destination&stringByAppendingPathComponent:&thumbnailName];&&&&&&&&&&&&&&&&&&&&&&&&&dispatch_group_async(group,&ioQueue,&BlockWithAutoreleasePool(^{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[thumbnailData&writeToFile:&thumbnailPath&atomically:&NO];&&&&&&&&&&&&&&&&&&&&&&&&&}));&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}));&&&&&&&&&}));&&&&&}&}&dispatch_group_wait(group,&DISPATCH_TIME_FOREVER);&
这个就是我们的&imagegcd3.m.
GCD使得我们很容易就将任务的不同部分放入相同的队列中去(简单地嵌套一下dispatch)。这次我们的程序将会表现地很好。。。我是说多数情况。。。。
问题在于任务中的不同部分不是同步的,导致了整个程序的不稳定。我们的新程序的整个流程如下:
Main Thread
Concurrent Queue
find paths
-----------&
write &-----------
图中的箭头是非阻塞的,并且会简单地将内存中的对象进行缓冲。
现在假设一个机器的磁盘足够快,快到比CPU处理任务(也就是图片处理)要快。其实不难想象:虽然CPU的动作很快,但是它的工作更繁重,解码、压缩、编码。从磁盘读取的数据开始填满IO
queue,数据会占用内存,很可能越占越多(如果你的~/Pictures中有很多很多图片的话)。
然后你就会内存爆仓,然后开始虚拟内存交换。。。又来了。。
这就会像第一次一样导致恶性循环。一旦任何东西导致工作线程阻塞,GCD就会创建更多的线程,这个线程执行的任务又会占用内存(从磁盘读取的数据),然后又开始交换内存。。
结果:这个程序要么就是运行地很顺畅,要么就是很低效。
注意如果磁盘速度比较慢的话,这个问题依旧会出现,因为缩略图会被缓冲在内存里,不过这个问题导致的低效比较不容易出现,因为缩略图占的内存少得多。
真正的修复
由于上一次我们的尝试出现的问题在于没有同步不同部分的操作,所以让我写出同步的代码。最简单的方法就是使用信号量来限制同时执行的任务数量。
那么,我们需要限制为多少呢?
显然我们需要根据CPU的核数来限制这个量,我们又想马儿好又想马儿不吃草,我们就设置为cpu核数的两倍吧。不过这里只是简单地这样处理,GCD的作用之一就是让我们不用关心操作系统的内部信息(比如cpu数),现在又来读取cpu核数,确实不太妙。也许我们在实际应用中,可以根据其他需求来定义这个限制量。
现在我们的主循环代码就是这样了:
dispatch_queue_t&ioQueue&=&dispatch_queue_create(&com.mikeash.imagegcd.io&,&NULL);&&int&cpuCount&=&[[NSProcessInfo&processInfo]&processorCount];&dispatch_semaphore_t&jobSemaphore&=&dispatch_semaphore_create(cpuCount&*&2);&&dispatch_group_t&group&=&dispatch_group_create();&__block&uint32_t&count&=&-1;&for(NSString&*path&in&enumerator)&{&&&&&WithAutoreleasePool(^{&&&&&&&&&if([[[path&pathExtension]&lowercaseString]&isEqual:&@&jpg&])&&&&&&&&&{&&&&&&&&&&&&&NSString&*fullPath&=&[dir&stringByAppendingPathComponent:&path];&&&&&&&&&&&&&&&&&&&&&&&&&&dispatch_semaphore_wait(jobSemaphore,&DISPATCH_TIME_FOREVER);&&&&&&&&&&&&&&&&&&&&&&dispatch_group_async(group,&ioQueue,&BlockWithAutoreleasePool(^{&&&&&&&&&&&&&&&&&NSData&*data&=&[NSData&dataWithContentsOfFile:&fullPath];&&&&&&&&&&&&&&&&&dispatch_group_async(group,&globalQueue,&BlockWithAutoreleasePool(^{&&&&&&&&&&&&&&&&&&&&&NSData&*thumbnailData&=&ThumbnailDataForData(data);&&&&&&&&&&&&&&&&&&&&&if(thumbnailData)&&&&&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailName&=&[NSString&stringWithFormat:&@&%d.jpg&,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&OSAtomicIncrement32(&)];&&&&&&&&&&&&&&&&&&&&&&&&&NSString&*thumbnailPath&=&[destination&stringByAppendingPathComponent:&thumbnailName];&&&&&&&&&&&&&&&&&&&&&&&&&dispatch_group_async(group,&ioQueue,&BlockWithAutoreleasePool(^{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[thumbnailData&writeToFile:&thumbnailPath&atomically:&NO];&&&&&&&&&&&&&&&&&&&&&&&&&&&&&dispatch_semaphore_signal(jobSemaphore);&&&&&&&&&&&&&&&&&&&&&&&&&}));&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&else&atch_semaphore_signal(jobSemaphore);&&&&&&&&&&&&&&&&&}));&&&&&&&&&&&&&}));&&&&&&&&&}&&&&&});&}&dispatch_group_wait(group,&DISPATCH_TIME_FOREVER);&
最终我们写出了一个能平滑运行且又快速处理的程序。
我测试了一些运行时间,对7913张图片:
处理时间 (秒)
imagegcd1.m
imagegcd2.m
没运行,这个还是别运行了
imagegcd3.m
imagegcd4.m
注意,因为我比较懒。所以我在运行这些测试的时候,没有关闭电脑上的其他程序。。。严格的进行对照的话,实在是太蛋疼了。。
所以这个数值我们只是参考一下。
比较有意思的是,3和4的执行状况差不多,大概是因为我电脑有15g可用内存吧。。。内存比较小的话,这个imagegcd3应该跑的很吃力,因为我发现它使用最多的时候,占用了10g内存。而4的话,没有占多少内存。
GCD是个比较范特西的技术,可以办到很多事儿,但是它不能为你办所有的事儿。所以,对于进行IO操作并且可能会使用大量内存的任务,我们必须仔细斟酌。当然,即使这样,GCD还是为我们提供了简单有效的方法来进行并发计算。
【编辑推荐】
【责任编辑: TEL:(010)】
关于&&&&的更多文章
多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行
既然强大的Android Studio来了,有什么理由不去用呢?
在iOS开发过程中,尤其是对于新手来说,都会遇到或多
Web App开发中会面临越来越“重”的问题,如果在开始
作为Android开发者,最头疼是什么?相信大家会异口同
本书从计算机网络安全的概念入手,分析了单机节点、单一网络、互联网络和开放互联网络的基本安全问题,并对计算机网络安全体系架
Windows Phone专家
Android开发专家
51CTO旗下网站GCD NSOperation NSTread总结:_iOS(Objective-C)编程技术博客_天涯博客
讨论学习iOS开发的基础技术。
今日访问:[$DayVisitCount$]
总访问量:68
开博时间:
博客排名:暂无排名
GCD&NSOperation&NSTread总结:
进程:进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
线程:1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行,比如使用酷狗播放音乐、使用迅雷下载电影,都需要在线程中执行。1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务,也就是说,在同一时间内,1个线程只能执行1个任务,比如在1个线程中下载3个文件(分别是文件A、文件B、文件C)。
多线程:1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务,进程&-&车间,线程-&车间工人,多线程技术可以提高程序的执行效率,比如同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C)。
多线程的原理
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)?多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)?如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象?思考:如果线程非常非常多,会发生什么情况??CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源?每条线程被调度执行的频次会降低(线程的执行效率降低)
相同之处:同时处理多线程
NSTread优缺点:
优点:比起NSOperation GCD 更加轻量级&& 缺点:需要自己手动管理线程的周期和线程同步。
创建方式两种:
[NSThread detachNewThreadSelector:@selector(updateAnimation) toTarget:self withObject:nil];
1.NSThread*&myThread&=&[[NSThread&alloc]&initWithTarget:self&&selector:@selector(doSomething:)&&object:nil];&&
2.[myThread&start];&
不显式创建线程的方法:
用NSObject的类方法 &performSelectorInBackground:withObject: 创建一个线程:?[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];
回到主线程
[self&performSelectorOnMainThread:@selector(updateUI:)&withObject:image&waitUntilDone:YES];
NSOperation&
优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation 相关的类是&NSOperation ,NSOperationQueue。NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation(操作对象默认在主线程中执行,只有添加到队列中才会开启新的线程。) 和 NSBlockOperation(NSBlockOperation封装的操作数大于1,就会异步执行操作)。创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
一种是用定义好的两个子类:
NSInvocationOperation 和 NSBlockOperation。
另一种是继承NSOperation
如果你也熟悉Java,NSOperation就和java.lang.Runnable接口很相似。和Java的Runnable一样,NSOperation也是设计用来扩展的,只需继承重写NSOperation的一个方法main。相当与java 中Runnalbe的Run方法。然后把NSOperation子类的对象放入NSOperationQueue队列中,该队列就会启动并开始处理它。
继承NSOperation
在.m文件中实现main方法,main方法编写要执行的代码即可。
队列里可以加入很多个NSOperation,&可以把NSOperationQueue看作一个线程池,可往线程池中添加操作(NSOperation)到队列中。线程池中的线程可看作消费者,从队列中取走操作,并执行它。
通过下面的代码设置:?[queue setMaxConcurrentOperationCount:5];?线程池中的线程数,也就是并发操作数。默认情况下是-1,-1表示没有限制,这样会同时运行队列中的全部的操作。
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。
Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统。这建立在任务并行执行的线程池模式的基础上的。它首次发布在Mac OS X 10.6 ,iOS 4及以上也可用。
GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。
一个任务可以是一个函数(function)或者是一个。&GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。
GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行
使用GCD进行编程,开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。其中,&定义想执行的任务&使用Block语法来完成。Dispatch Queue中的任务按照FIFO(先入先出)的顺序进行处理,也就是先进入的任务先处理。另外,Dispatch Queue分为串行和并行两种。
Serial VS Concurrent
Serial Dispatch Queue要求等待正在执行的任务完成,再执行下一个,也就是说只会创建一个线程来执行任务
Concurrent Dispatch Queue中后面的任务可以不必等待正在执行的任务执行完成就可以开始执行,也就是同时可以执行多个任务,也就是说会创建多个线程同时执行多个任务
一般情况下:如果要求在不能改变执行顺序或不想并行执行多个任务时使用Serial Dispatch Queue。
简言之:Serial Dispatch Queue串行代码;Concurrent Dispatch Queue并行代码。
Main& Dispatch Queue & Global Dispatch Queue
系统为我们提供了两种调度队列 它们不需要我们创建 只是在我们使用时获取一下
dispatch_sync 同步添加操作,它是等待操作完成之后再继续执行。
dispatch_async& 异步添加进任务队列,它不会做任何等待
同步提交任务需注意:调用此函数的队列和同步提交任务的目标队列都是当前队列话 会造成死锁iOS中如果用GCD方式创建两个队列,这两个队列的执行方式是啥_ios开发吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
iOS中如果用GCD方式创建两个队列,这两个队列的执行方式是啥收藏
看你是创建串行队列还是并行队列,是同步还是异步创建个并行队列方式如下:dispatch_queue_t q=dispatch_queue_t(dispatch
CONCURRENT)一般来说在主线程中刷新表格和数据,一般异步请求(这边代码记不清了)dispatch_async
=(dispatch_main_queue)^{NSLog(&%@,self.tableview.textlable) [self.tableview reloadData])}
我不明白 如果有两个队列 系统是如何执行的
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或3095人阅读
Objective-C(47)
& & & & GCD是Grand Central Dispatch的缩写。其包含了语言特性、runtime libraries以及提供系统级、综合提高的系统增强功能在iOS和OSX 系统上多核的硬件来支持并行执行代码。GCD会负责创建线程和调度执行你写的功能代码。系统直接提供线程管理,比应用添加线程更加高效,因此使用GCD能够带来很多好处,例如使用简单、而且更加高效,允许你同步或者一部执行任意的代码block。但是使用它也必须注意一些问题,由于其实现是基于c语言的API,因此没有异常捕获、异常处理机制,所以它不能捕获高层语言产生的异常。使用GCD时必须在将block提交到dispatch
queue中之前捕获所有异常,并解决所有异常。
& & & & 说到底,GCD就是系统来帮用户来管理线程,而不需要再编写线程代码。程序员只需要专心编写执行某项功能的代码,添加到block或方法(函数)中,然后可以有下面两种方式来处理block或方法(函数)。
& & & & 1.直接将block加入到dispatch queues,
& & & & 2.将Dispatch source封装为一个特定类型的系统事件,当系统事件发生时提交一个特定的block对象或函数到dispatch queue,
& & & & 然后,Dispatch queue 按先进先出的顺序, 串行或并发地执行任务。
& & & & 这里,Dispatch queue是一个基于C的执行自定义任务机制,而Dispatch source是基于C的系统事件异步处理机制,一般Dispatch
source封装一个特定类型的系统事件,该事件作为某个特定的block对象或函数提交到Dispatch queue中的前提条件。Dispatch source可以监控的系统事件类型有:&
&  定时器
&  信号处理器&
& & & &&描述符相关的事件
& 进程相关的事件
&  Mach port事件
&  你触发的自定义事件&
& & & & 而对于Dispatch Queues,其可以分为三种:
& & & &&&串行Queue
& & & &&&&并发队列
& & & &&main dispatch queue
& & & & 如果使用 dispatch queue,与执行相同功能的多线程相比,最直接的优点是简单,不用编写线程创建和管理的代码,让你集中精力编写实际工作的代码。另外系统管理线程更加高效,并且可以动态调控所有线程。& & & &&
& & & &串行Queue,也称为private dispatch queue,其每次只执行一个任务,按任务添加顺序执行。当前正在执行的任务在独立的线程中运行(注意:不同任务的线程可能不同),dispatch
queue 管理了这些线程。 通常串行 queue 主要用于对特定资源的同步访问。 你可以创建任意数量的串行queues,虽然每个 queue 本身每 次只能执行一个任务,但是各个 queue 之间是并发执行的。
& & & &并行Queue,也称为 global dispatch queue, 它可以并发执行一个或多个任务,但是所要执行的任务仍然是以添加到 queue 的顺序启动。每个任务运行于独立的线程中,dispatch queue 管理所有线程。同时运行的任务数量随时都会变化,而且依赖于系统条件。值得注意的是,前往不要创建并发
dispatch queues,相反只能使用三个已经定义好的全局并发 queues。
& & & &main Dispatch Queue,其实是串行的 queue,不过在应用主线程中执行任务,而且全局可用。这个queue
与应用的 run loop 交叉执行。由于它运行在应用的主?线程,main queue 通常用于应用的关键同步点。 虽然你不需要创建 main dispatch queue,但你必须确保应用适当地回收。
& & & & 从上面的三种Dispatch Queue可以看出,queue中的任务基本上都是按照添加到queue中的顺序来执行的,因此Dispatch queue 比线程具有更强的可预测性,这种可预测性能够有效的减少程序出错的可能性,而且有效的避免死锁出现。例如两个线程访问共享资源,你可能无法控制哪个线程先后访问;但是把两个任务添加到串行
queue,则可以确保两个任务对共享资源的访问顺序。同时基于 queue 的同步也比基于锁的线程同步机制更加高效。&
& & & & 使用dispatch queues 还需要注意的关键问题有:
& & & & 1. Dispatch queues 相对其它 dispatch queues 并发地执行任务,串行化任务只能在同一个 dispatch queue 中实现。
& & & & 2. 系统决定了同时能够执行的任务数量,应用在 100 个不同的 queues 中启动 100 个任务,并不表示 100 个任务全部都在并发地执行(除非系统拥有100 或更多个核)&
& & & & 3. 系统在选择执行哪个任务时,会考虑 queue 的优先级。
& & & & 4. Queue中的任务必须在任何时候都准备好运行,注意这点和Operation对象不同。
& & & & 5. 串行 dispatch queue 是引用计数的对象。因此使用它需要 retain 这些 queue,另外 dispatch source 也可能添加到一个 queue,从而增加 retain的计数。因此你必须确保所有 dispatch source 都被取消,而且适当地调用release。 (如果使用了ARC,那么这些就无所谓了!)
& & & & 说了这么多,还没有个例子,似乎很不爽,下面就来个例子!
& & & & &1.定义一个dispatch_get_global_queue:
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//定义一个dispatch_get_global_queue的优先级,以及保留给未来使用的flag值,一般传入的是0。
2.定义一个block,执行真正需要实现的某项功能:
void (^ex)() = ^ {
NSlog(@&it's example!&);
注意这里的block是dispatch_block_t,dispatch_block_t的要求是:The
prototype of blocks submitted to dispatch queues, which take no arguments and have no return value.,可以看到其为无行参也无返回类型的block。其基本形式为:
typedef void (^dispatch_block_t)(void);
3.将block加入到dispatch queue
dispatch_async(aQueue , ex);//
可以看到,上面就是使用GCD dispatch queue的例子,代码实现非常简单。但是我们不能因为它使用简单就随意使用,是否使用GCD,主要看其block所执行的功能。设计block需要考虑以下问题:
1.尽管 Queue 执行小任务比原始线程更加高效,仍然存在创建 Block 和在 Queue 中执行的开销。如果 Block 做的事情太少,可能直接执行比 dispatch 到 queue 更加有效。使用性能工具来确认 Block
的工作是否太少&——设计block和是否使用dispatch queue主要关注的点。
2.对于使用dispatch queue 的异步Block,可以在 Block 中安全地捕获和使用父函数或方法中的 scalar 变量。但是 Block不应该去捕获大型结构体或其它基于指针的变量,这些变量由 Block 的调用上下文分配和删除。在你的 Block 被执行时,这些指针引用的内存可能已经不存在。当然,你自己显
式地分配内存(或对象),然后让 Block 拥有这些内存的所有权,是安全 可行的。&
3.Dispatch queue对添加的Block会进行复制,在完成执行后自动释放。也就是你不需要在添加 Block 到 Queue 时显式地复制。
4.绝对不要针对底层线程缓存数据,然后期望在不同 Block 中能够访问这些 数据。如果相同 queue 中的任务需要共享数据,应该使用
dispatch queue 的 context 指针来存储这些数据。&
5.如果Block创建了大量Objective-C对象,考虑创建自己的autoreleasepool, 来处理这些对象的内存管理。虽然 GCD dispatch queue 也有自己的 autorelease pool,但不保证在什么时候会回收这些 pool。
& & & & GCD属于iOS并行编程的主要部分,具体的内容可以参考:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:709325次
积分:7267
积分:7267
排名:第1884名
原创:123篇
转载:35篇
评论:63条
(2)(2)(5)(8)(1)(1)(1)(1)(1)(1)(1)(4)(2)(5)(3)(12)(6)(8)(24)(26)(17)(33)}

我要回帖

更多关于 串行口和并行口 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信