gcd介绍(一):基本概念和dispatch queue
本文为大家介绍gcd的基本概念和dispatch queue。现在你需要知道gcd的基本概念,怎样创建dispatch queue,怎样提交job至dispatch queue以及怎样将队列用作线程同步。
什么是gcd?
grand central dispatch或者gcd,是一套低层api,提供了一种新的方法来进行并发程序编写。从基本功能上讲,gcd有点像nsoperationqueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。gcd比之nsopertionqueue更底层更高效,并且它不是cocoa框架的一部分。
除了代码的平行执行能力,gcd还提供高度集成的事件控制系统。可以设置句柄来响应文件描述符、mach ports(mach port用于 os x上的进程间通讯)、进程、计时器、信号、用户生成事件。这些句柄通过gcd来并发执行。
gcd的api很大程度上基于block,当然,gcd也可以脱离block来使用,比如使用传统c机制提供函数指针和上下文指针。实践证明,当配合block使用时,gcd非常简单易用且能发挥其最大能力。
你可以在mac上敲命令“man dispatch”来获取gcd的文档。
为何使用?
gcd提供很多超越传统多线程编程的优势:
易用:gcd比之thread跟简单易用。由于gcd基于work unit而非像thread那样基于运算,所以gcd可以控制诸如等待任务结束、监视文件描述符、周期执行代码以及工作挂起等任务。基于block的血统导致它能极为简单得在不同代码作用域之间传递上下文。
效率:gcd被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。这关系到易用性:导致gcd易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了。
性能:gcd自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。
dispatch objects
尽管gcd是纯c语言的,但它被组建成面向对象的风格。gcd对象被称为dispatch object。dispatch object像cocoa对象一样是引用计数的。使用dispatch_release和dispatch_retain函数来操作dispatch object的引用计数来进行内存管理。但主意不像cocoa对象,dispatch object并不参与垃圾回收系统,所以即使开启了gc,你也必须手动管理gcd对象的内存。
dispatch queues 和 dispatch sources(后面会介绍到)可以被挂起和恢复,可以有一个相关联的任意上下文指针,可以有一个相关联的任务完成触发函数。可以查阅“man dispatch_object”来获取这些功能的更多信息。
dispatch queues
gcd的基本概念就是dispatch queue。dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatch queue可以是并发的或串行的。并发任务会像nsoperationqueue那样基于系统负载来合适地并发进行,串行队列同一时间只执行单一任务。
gcd中有三种队列类型:
the main queue:与主线程功能相同。实际上,提交至main queue的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。
global queues:全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。
用户队列:用户队列 (gcd并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数dispatch_queue_create创建的队列. 这些队列是串行的。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mutex。
创建队列
要使用用户队列,我们首先得创建一个。调用函数dispatch_queue_create就行了。函数的第一个参数是一个标签,这纯是为了debug。apple建议我们使用倒置域名来命名队列,比如“com.dreamingwish.subsystem.task”。这些名字会在崩溃日志中被显示出来,也可以被调试器调用,这在调试中会很有用。第二个参数目前还不支持,传入null就行了。
提交 job
向一个队列提交job很简单:调用dispatch_async函数,传入一个队列和一个block。队列会在轮到这个block执行时执行这个block的代码。下面的例子是一个在后台执行一个巨长的任务:
dispatch_async(dispatch_get_global_queue(dispatch_queue_priority_default,0),^{[selfgodosomethinglongandinvolved];nslog(@"donedoingsomethinglongandinvolved");});
dispatch_async函数会立即返回, block会在后台异步执行。
当然,通常,任务完成时简单地nslog个消息不是个事儿。在典型的cocoa程序中,你很有可能希望在任务完成时更新界面,这就意味着需要在主线程中执行一些代码。你可以简单地完成这个任务——使用嵌套的dispatch,在外层中执行后台任务,在内层中将任务dispatch到main queue:
dispatch_async(dispatch_get_global_queue(dispatch_queue_priority_default,0),^{[selfgodosomethinglongandinvolved];dispatch_async(dispatch_get_main_queue(),^{[textfieldsetstringvalue:@"donedoingsomethinglongandinvolved"];});});
还有一个函数叫dispatch_sync,它干的事儿和dispatch_async相同,但是它会等待block中的代码执行完成并返回。结合 __block类型修饰符,可以用来从执行中的block获取一个值。例如,你可能有一段代码在后台执行,而它需要从界面控制层获取一个值。那么你可以使用dispatch_sync简单办到:
__blocknsstring*stringvalue;dispatch_sync(dispatch_get_main_queue(),^{//__blockvariablesaren\\\’tautomaticallyretained//sowe\\\’dbettermakesurewehaveareferencewecankeepstringvalue=[[textfieldstringvalue]copy];});[stringvalueautorelease];//usestringvalueinthebackgroundnow
我们还可以使用更好的方法来完成这件事——使用更“异步”的风格。不同于取界面层的值时要阻塞后台线程,你可以使用嵌套的block来中止后台线程,然后从主线程中获取值,然后再将后期处理提交至后台线程:
dispatch_queue_tbgqueue=myqueue;dispatch_async(dispatch_get_main_queue(),^{nsstring*stringvalue=[[[textfieldstringvalue]copy]autorelease];dispatch_async(bgqueue,^{//usestringvalueinthebackgroundnow});});
取决于你的需求,myqueue可以是用户队列也可以使全局队列。
不再使用锁(lock)
用户队列可以用于替代锁来完成同步机制。在传统多线程编程中,你可能有一个对象要被多个线程使用,你需要一个锁来保护这个对象:
nslock*lock;
访问代码会像这样:
-(id)something{idlocalsomething;[locklock];localsomething=[[somethingretain]autorelease];[lockunlock];returnlocalsomething;}-(void)setsomething:(id)newsomething{[locklock];if(newsomething!=something){[somethingrelease];something=[newsomethingretain];[selfupdatesomethingcaches];}[locku
美国域名注册,有什么不可忽略的细节?的备案我已经提交好几天了地产网站制作需要注意哪些方面?千牛挂在云服务器上有影响吗90分钟送到家 圣罗兰进军中国在线零售二手域名交易平台排名榜哪家好?购买二手域名需要注意什么?网站如何换二级域名,换域名对网站的影响有哪些?根据要求提交的我个人身份证原件不符合要求