iOS并发编程
2016-03-08 13:44
666 查看
iOS和OSX使用异步设计方法(asynchronousdesignapproach)去解决并发的问题,而不是直接使用线程。以下是两种解决并发执行的方法:
GrandCentralDispatch(GCD)
GCD帮组管理线程代码并把这部分代码移到系统层次。开发者需要做的只是定义需要完成的任务然后把其放到合适的dispatchqueue(下面简写为DQ)中。GCD负责创建必须的线程和在这些线程上调度任务。
Operationqueues
OperationQueues(下面简写为OQ)是Obj-C的对象,工作原理基本和DQ一样。所有的线程管理都由OQ管理。
一些重要概念的简单解释:
系统事件包括:
Timers
Signalhandlers
Descriptor-relatedevents
Process-relatedevents
Machportevents
Customeventsthatyoutrigger
Considercomputingvaluesdirectlywithinyourtaskifmemoryusageisafactor.Ifyourapplicationisalreadymemorybound,computingvaluesdirectlynowmaybefasterthanloadingcachedvaluesfrommainmemory.Computingvaluesdirectlyusestheregistersandcachesofthegivenprocessorcore,whicharemuchfasterthanmainmemory.Ofcourse,youshouldonlydothisiftestingindicatesthisisaperformancewin.
Identifyserialtasksearlyanddowhatyoucantomakethemmoreconcurrent.Ifataskmustbeexecutedseriallybecauseitreliesonsomesharedresource,considerchangingyourarchitecturetoremovethatsharedresource.Youmightconsidermakingcopiesoftheresourceforeachclientthatneedsoneoreliminatetheresourcealtogether.
Avoidusinglocks.Thesupportprovidedbydispatchqueuesandoperationqueuesmakeslocksunnecessaryinmostsituations.Insteadofusinglockstoprotectsomesharedresource,designateaserialqueue(oruseoperationobjectdependencies)toexecutetasksinthecorrectorder.
Relyonthesystemframeworkswheneverpossible.Thebestwaytoachieveconcurrencyistotakeadvantageofthebuilt-inconcurrencyprovidedbythesystemframeworks.Manyframeworksusethreadsandothertechnologiesinternallytoimplementconcurrentbehaviors.Whendefiningyourtasks,looktoseeifanexistingframeworkdefinesafunctionormethodthatdoesexactlywhatyouwantanddoessoconcurrently.UsingthatAPImaysaveyoueffortandismorelikelytogiveyouthemaximumconcurrencypossible.
Table3-1Typesofdispatchqueues
DQ的好处是让你更多关注的是执行的任务,而不是去关心线程的创建和管理。
如果有两个在不同线程的任务需要访问一个共享的资源,如果使用线程需要使用锁去实现这个操作。如果使用DQ,可以把任务放到serialdispatchqueue中去,保证一个时刻只有一个任务在访问资源。这相对于使用锁去同步,有很多的性能优势。锁请求昂贵的kerneltrap不管是否抢占资源,但是DQ工作于用户进程空间,只有在必须的时候才会向内核调用。
对于运行在串行队列的任务,并不是并发的,但相对于使用lock的情况,线程的并发优势会大大降低甚至没有了,更重要的是,线程的创建消耗内核和用户空间的内存。DQ不用为它们的线程牺牲同样的内存,而且DQ使用的线程会保持繁忙并且是不阻塞的。
DQ的一些关键点:
Dispatchqueuesexecutetheirtasksconcurrentlywithrespecttootherdispatchqueues.Theserializationoftasksislimitedtothetasksinasingledispatchqueue.多DQ之间任务是并发的,单DQ内任务是串行的。
Thesystemdeterminesthetotalnumberoftasksexecutingatanyonetime.Thus,anapplicationwith100tasksin100differentqueuesmaynotexecuteallofthosetasksconcurrently(unlessithas100ormoreeffectivecores).
Thesystemtakesqueueprioritylevelsintoaccountwhenchoosingwhichnewtaskstostart.Forinformationabouthowtosetthepriorityofaserialqueue,see“ProvidingaCleanUpFunctionForaQueue.”
Tasksinaqueuemustbereadytoexecuteatthetimetheyareaddedtothequeue.(IfyouhaveusedCocoaoperationobjectsbefore,noticethatthisbehaviordiffersfromthemodeloperationsuse.)
Privatedispatchqueuesarereference-countedobjects.Inadditiontoretainingthequeueinyourowncode,beawarethatdispatchsourcescanalsobeattachedtoaqueueandalsoincrementitsretaincount.Thus,youmustmakesurethatalldispatchsourcesarecanceledandallretaincallsarebalancedwithanappropriatereleasecall.Formoreinformationaboutretainingandreleasingqueues,see“MemoryManagementforDispatchQueues.”Formoreinformationaboutdispatchsources,see“AboutDispatchSources.”
除了DQ外,GCD提供以下技术使用queue去管理代码:
Table3-2Technologiesthatusedispatchqueues
Block使用的一些原则:
Forblocksthatyouplantoperformasynchronouslyusingadispatchqueue,itissafetocapturescalarvariablesfromtheparentfunctionormethodandusethemintheblock.However,youshouldnottrytocapturelargestructuresorotherpointer-basedvariablesthatareallocatedanddeletedbythecallingcontext.Bythetimeyourblockisexecuted,thememoryreferencedbythatpointermaybegone.Ofcourse,itissafetoallocatememory(oranobject)yourselfandexplicitlyhandoffownershipofthatmemorytotheblock.对于block,使用父方法的数量值是安全的,不要使用callingcontext分配和删除的大的结构体或者指针。当block执行的时候,那些指针指向的内存可能已经不存在了。
Dispatchqueuescopyblocksthatareaddedtothem,andtheyreleaseblockswhentheyfinishexecuting.Inotherwords,youdonotneedtoexplicitlycopyblocksbeforeaddingthemtoaqueue.DQ会复制添加到它们的block。
Althoughqueuesaremoreefficientthanrawthreadsatexecutingsmalltasks,thereisstilloverheadtocreatingblocksandexecutingthemonaqueue.Ifablockdoestoolittlework,itmaybecheapertoexecuteitinlinethandispatchittoaqueue.Thewaytotellifablockisdoingtoolittleworkistogathermetricsforeachpathusingtheperformancetoolsandcomparethem.不要让block执行开销比dispatch它到queue上还少
Donotcachedatarelativetotheunderlyingthreadandexpectthatdatatobeaccessiblefromadifferentblock.Iftasksinthesamequeueneedtosharedata,usethecontextpointerofthedispatchqueuetostorethedatainstead.Formoreinformationonhowtoaccessthecontextdataofadispatchqueue,see“StoringCustomContextInformationwithaQueue.”
使用DQ的上下文指针(contextpointer)去保存block之间的共享数据,而不是在一个block中缓存它并在另一个block中访问
IfyourblockcreatesmorethanafewObjective-Cobjects,youmightwanttoenclosepartsofyourblock’scodeinan@autoreleaseblocktohandlethememorymanagementforthoseobjects.AlthoughGCDdispatchqueueshavetheirownautoreleasepools,theymakenoguaranteesastowhenthosepoolsaredrained.Ifyourapplicationismemoryconstrained,creatingyourownautoreleasepoolallowsyoutofreeupthememoryforautoreleasedobjectsatmoreregularintervals.
如果在block中创建大量的Obj-C对象,请使用@autorelease
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_set_context
dispatch_get_context
系统不会使用这些自定义的数据,这些数据的分配和析构都取决于你。
Listing3-3Installingaqueuecleanupfunction
不要在执行中的任务中调用dispatch_sync和
Important:Likearegular
Becausetheyeffectivelyblockthecurrentthread,youshouldalsobecarefulwhencallingthesefunctionsfromyourmainthread,wheretheycouldpreventyoureventhandlingloopfromrespondingtoeventsinatimelymanner.Ifyourloopcoderequiresanoticeableamountofprocessingtime,youmightwanttocallthesefunctionsfromadifferentthread.
使用dispatchsemaphores的顺序:
Whenyoucreatethesemaphore(usingthe
Ineachtask,call
Whenthewaitcallreturns,acquiretheresourceanddoyourwork.
Whenyouaredonewiththeresource,releaseitandsignalthesemaphorebycallingthe
Dispatchqueuesthemselvesarethreadsafe.Inotherwords,youcansubmittaskstoadispatchqueuefromanythreadonthesystemwithoutfirsttakingalockorsynchronizingaccesstothequeue.
Donotcallthe
Avoidtakinglocksfromthetasksyousubmittoadispatchqueue.Althoughitissafetouselocksfromyourtasks,whenyouacquirethelock,youriskblockingaserialqueueentirelyifthatlockisunavailable.Similarly,forconcurrentqueues,waitingonalockmightpreventothertasksfromexecutinginstead.Ifyouneedtosynchronizepartsofyourcode,useaserialdispatchqueueinsteadofalock.
Althoughyoucanobtaininformationabouttheunderlyingthreadrunningatask,itisbettertoavoiddoingso.Formoreinformationaboutthecompatibilityofdispatchqueueswiththreads,see“CompatibilitywithPOSIXThreads.”
Timerdispatchsourcesgenerateperiodicnotifications.
SignaldispatchsourcesnotifyyouwhenaUNIXsignalarrives.
Descriptorsourcesnotifyyouofvariousfile-andsocket-basedoperations,suchas:
Whendataisavailableforreading
Whenitispossibletowritedata
Whenfilesaredeleted,moved,orrenamedinthefilesystem
Whenfilemetainformationchanges
Processdispatchsourcesnotifyyouofprocess-relatedevents,suchas:
Whenaprocessexits
Whenaprocessissuesa
Whenasignalisdeliveredtotheprocess
MachportdispatchsourcesnotifyyouofMach-relatedevents.
Customdispatchsourcesareonesyoudefineandtriggeryourself.
dispatchsource取代异步回调方法,实现响应相关的系统事件。配置dispatchsource时,需要指定监听的事件,DQ和用于处理事件的代码。当关心的事件发生时,dispatchsource会向指定的DQ提交block或者函数。
dispatchsource会retain相关联的DQ防止其被释放。
为防止DQ事件积压,dispatchsources实现了事件合并方案。如果在旧事件没有被处理或者执行前,新事件到达了,dispatchsource会合并两个事件的数据。根据事件类型,可能会取代旧事件或者更新旧事件中的数据。
Createthedispatchsourceusingthe
Configurethedispatchsource:
Assignaneventhandlertothedispatchsource;see“WritingandInstallinganEventHandler.”
Fortimersources,setthetimerinformationusingthe“CreatingaTimer.”
Optionallyassignacancellationhandlertothedispatchsource;see“InstallingaCancellationHandler.”
Callthe“SuspendingandResumingDispatchSources.”
dispatch_source_set_event_handler_f去设置事件handler。当事件发生,事件handler会被提交到DQ中等待处理。
两种类型的handler:
在事件handler内部,可以从DS中获取关于事件的相关信息。
事件handler中获取到的变量默认是只读的。虽然block特性支持修改这些变量在特定的情况下,但是不要尝试这样做。DS是异步执行事件handler的,所以内部获取的变量可能在执行的时候已经不存在。
以下方法,可以获取事件的信息:
Table4-1Gettingdatafromadispatchsource
可以通过
这个操作时异步的,并不能保证即时执行。对于已经出列的在等待处理的事件handler,它会继续在前一个queue里面执行。
dispatch_set_context函数去关联自定义的数据到DS上。可以使用上文件指针去存储存储数据,如果存储了,旧必须要设置cancellationhandler去释放它。
GrandCentralDispatch(GCD)
GCD帮组管理线程代码并把这部分代码移到系统层次。开发者需要做的只是定义需要完成的任务然后把其放到合适的dispatchqueue(下面简写为DQ)中。GCD负责创建必须的线程和在这些线程上调度任务。
Operationqueues
OperationQueues(下面简写为OQ)是Obj-C的对象,工作原理基本和DQ一样。所有的线程管理都由OQ管理。
一些重要概念的简单解释:
DispatchQueues
DQ是执行自定义任务的基于C的机制。DQ不管是串行还是并行,都是FIFO的。DispatchSources
dispatchsource是用于处理系统各种特定类型事件的机制。DS封装一些必要的信息,例如系统事件的类型,并会在事件发生的时候,向DQ提交一个特定的block或者函数。系统事件包括:
Timers
Signalhandlers
Descriptor-relatedevents
Process-relatedevents
Machportevents
Customeventsthatyoutrigger
OperationQueues
OQ是在Cocoa框架中等价于并发dispatchqueue的东西,它实现NSOperationQueue。相对于DQ按FIFO的顺序执行任务,OQ引入其他的因素决定执行顺序。最原始的一个因素是任务的执行是否受另外一些任务执行完成的影响。在定义任务的时候配置这些因素并且使用它们去创建一个复杂的执行顺序路径。TipsforImprovingEfficiency
Inadditiontosimplyfactoringyourcodeintosmallertasksandaddingthemtoaqueue,thereareotherwaystoimprovetheoverallefficiencyofyourcodeusingqueues:Considercomputingvaluesdirectlywithinyourtaskifmemoryusageisafactor.Ifyourapplicationisalreadymemorybound,computingvaluesdirectlynowmaybefasterthanloadingcachedvaluesfrommainmemory.Computingvaluesdirectlyusestheregistersandcachesofthegivenprocessorcore,whicharemuchfasterthanmainmemory.Ofcourse,youshouldonlydothisiftestingindicatesthisisaperformancewin.
Identifyserialtasksearlyanddowhatyoucantomakethemmoreconcurrent.Ifataskmustbeexecutedseriallybecauseitreliesonsomesharedresource,considerchangingyourarchitecturetoremovethatsharedresource.Youmightconsidermakingcopiesoftheresourceforeachclientthatneedsoneoreliminatetheresourcealtogether.
Avoidusinglocks.Thesupportprovidedbydispatchqueuesandoperationqueuesmakeslocksunnecessaryinmostsituations.Insteadofusinglockstoprotectsomesharedresource,designateaserialqueue(oruseoperationobjectdependencies)toexecutetasksinthecorrectorder.
Relyonthesystemframeworkswheneverpossible.Thebestwaytoachieveconcurrencyistotakeadvantageofthebuilt-inconcurrencyprovidedbythesystem
DispatchQueues
GCD自动提供一些DQ,当然也可以根据需求创建必须的自定义的。Type | Description |
---|---|
Serial | Serialqueues(alsoknownasprivatedispatchqueues)executeonetaskatatimeintheorderinwhichtheyareaddedtothequeue.Thecurrentlyexecutingtaskrunsonadistinctthread(whichcanvaryfromtasktotask)thatismanagedbythedispatchqueue.Serialqueuesareoftenusedtosynchronizeaccesstoaspecificresource. Youcancreateasmanyserialqueuesasyouneed,andeachqueueoperatesconcurrentlywithrespecttoallotherqueues.Inotherwords,ifyoucreatefourserialqueues,eachqueueexecutesonlyonetaskatatimebutuptofourtaskscouldstillexecuteconcurrently,onefromeachqueue.Forinformationonhowtocreateserialqueues,see |
Concurrent | Concurrentqueues(alsoknownasatypeofglobaldispatchqueue)executeoneormoretasksconcurrently,buttasksarestillstartedintheorderinwhichtheywereaddedtothequeue.Thecurrentlyexecutingtasksrunondistinctthreadsthataremanagedbythedispatchqueue.Theexactnumberoftasksexecutingatanygivenpointisvariableanddependsonsystemconditions. IniOS5andlater,youcancreateconcurrentdispatchqueuesyourselfbyspecifying DISPATCH_QUEUE_CONCURRENTasthequeuetype.Inaddition,therearefourpredefinedglobalconcurrentqueuesforyourapplicationtouse.Formoreinformationonhowtogettheglobalconcurrentqueues,see |
Maindispatchqueue | Themaindispatchqueueisagloballyavailableserialqueuethatexecutestasksontheapplication’smainthread.Thisqueueworkswiththeapplication’srunloop(ifoneispresent)tointerleavetheexecutionofqueuedtaskswiththeexecutionofothereventsourcesattachedtotherunloop.Becauseitrunsonyourapplication’smainthread,themainqueueisoftenusedasakeysynchronizationpointforanapplication. Althoughyoudonotneedtocreatethemaindispatchqueue,youdoneedtomakesureyourapplicationdrainsitappropriately.Formoreinformationonhowthisqueueismanaged,see |
如果有两个在不同线程的任务需要访问一个共享的资源,如果使用线程需要使用锁去实现这个操作。如果使用DQ,可以把任务放到serialdispatchqueue中去,保证一个时刻只有一个任务在访问资源。这相对于使用锁去同步,有很多的性能优势。锁请求昂贵的kerneltrap不管是否抢占资源,但是DQ工作于用户进程空间,只有在必须的时候才会向内核调用。
对于运行在串行队列的任务,并不是并发的,但相对于使用lock的情况,线程的并发优势会大大降低甚至没有了,更重要的是,线程的创建消耗内核和用户空间的内存。DQ不用为它们的线程牺牲同样的内存,而且DQ使用的线程会保持繁忙并且是不阻塞的。
DQ的一些关键点:
Dispatchqueuesexecutetheirtasksconcurrentlywithrespecttootherdispatchqueues.Theserializationoftasksislimitedtothetasksinasingledispatchqueue.多DQ之间任务是并发的,单DQ内任务是串行的。
Thesystemdeterminesthetotalnumberoftasksexecutingatanyonetime.Thus,anapplicationwith100tasksin100differentqueuesmaynotexecuteallofthosetasksconcurrently(unlessithas100ormoreeffectivecores).
Thesystemtakesqueueprioritylevelsintoaccountwhenchoosingwhichnewtaskstostart.Forinformationabouthowtosetthepriorityofaserialqueue,see
Tasksinaqueuemustbereadytoexecuteatthetimetheyareaddedtothequeue.(IfyouhaveusedCocoaoperationobjectsbefore,noticethatthisbehaviordiffersfromthemodeloperationsuse.)
Privatedispatchqueuesarereference-countedobjects.Inadditiontoretainingthequeueinyourowncode,beawarethatdispatchsourcescanalsobeattachedtoaqueueandalsoincrementitsretaincount.Thus,youmustmakesurethatalldispatchsourcesarecanceledandallretaincallsarebalancedwithanappropriatereleasecall.Formoreinformationaboutretainingandreleasingqueues,see
除了DQ外,GCD提供以下技术使用queue去管理代码:
Technology | Description |
---|---|
Dispatchgroups | Adispatchgroupisawaytomonitorasetof |
Dispatchsemaphores | Adispatchsemaphoreissimilartoatraditionalsemaphorebutisgenerallymoreefficient.Dispatchsemaphorescalldowntothekernelonlywhenthecallingthreadneedstobeblockedbecausethesemaphoreisunavailable.Ifthesemaphoreisavailable,nokernelcallismade.Foranexampleofhowtousedispatchsemaphores,see |
Dispatchsources | Adispatchsourcegeneratesnotificationsinresponsetospecifictypesofsystemevents.Youcanusedispatchsourcestomonitoreventssuchasprocessnotifications,signals,anddescriptoreventsamongothers.Whenaneventoccurs,thedispatchsourcesubmitsyourtaskcodeasynchronouslytothespecifieddispatchqueueforprocessing.Formoreinformationaboutcreatingandusingdispatchsources,see |
Forblocksthatyouplantoperformasynchronouslyusingadispatchqueue,itissafetocapturescalarvariablesfromtheparentfunctionormethodandusethemintheblock.However,youshouldnottrytocapturelargestructuresorotherpointer-basedvariablesthatareallocatedanddeletedbythecallingcontext.Bythetimeyourblockisexecuted,thememoryreferencedbythatpointermaybegone.Ofcourse,itissafetoallocatememory(oranobject)yourselfandexplicitlyhandoffownershipofthatmemorytotheblock.对于block,使用父方法的数量值是安全的,不要使用callingcontext分配和删除的大的结构体或者指针。当block执行的时候,那些指针指向的内存可能已经不存在了。
Dispatchqueuescopyblocksthatareaddedtothem,andtheyreleaseblockswhentheyfinishexecuting.Inotherwords,youdonotneedtoexplicitlycopyblocksbeforeaddingthemtoaqueue.DQ会复制添加到它们的block。
Althoughqueuesaremoreefficientthanrawthreadsatexecutingsmalltasks,thereisstilloverheadtocreatingblocksandexecutingthemonaqueue.Ifablockdoestoolittlework,itmaybecheapertoexecuteitinlinethandispatchittoaqueue.Thewaytotellifablockisdoingtoolittleworkistogathermetricsforeachpathusingtheperformancetoolsandcomparethem.不要让block执行开销比dispatch它到queue上还少
Donotcachedatarelativetotheunderlyingthreadandexpectthatdatatobeaccessiblefromadifferentblock.Iftasksinthesamequeueneedtosharedata,usethecontextpointerofthedispatchqueuetostorethedatainstead.Formoreinformationonhowtoaccessthecontextdataofadispatchqueue,see
使用DQ的上下文指针(contextpointer)去保存block之间的共享数据,而不是在一个block中缓存它并在另一个block中访问
IfyourblockcreatesmorethanafewObjective-Cobjects,youmightwanttoenclosepartsofyourblock’scodeinan@autoreleaseblocktohandlethememorymanagementforthoseobjects.AlthoughGCDdispatchqueueshavetheirownautoreleasepools,theymakenoguaranteesastowhenthosepoolsaredrained.Ifyourapplicationismemoryconstrained,creatingyourownautoreleasepoolallowsyoutofreeupthememoryforautoreleasedobjectsatmoreregularintervals.
如果在block中创建大量的Obj-C对象,请使用@autorelease
CreatingandManagingDispatchQueues
GettingtheGlobalConcurrentDispatchQueues
系统提供四条并发DQ。这四条DQ对于应用是全局的,它们之间的区别是优先级。CreatingSerialDispatchQueues
相对于并发DQ,串行DQ是要自己显式创建和管理的。StoringCustomContextInformationwithaQueue
所有的dispatch对象运行你关联自定义的上下文数据到该对象上。使用方法:系统不会使用这些自定义的数据,这些数据的分配和析构都取决于你。
ProvidingaCleanUpFunctionForaQueue
创建串行DQ可以指定回收函数在queue被析构的时候,去做自定义的清理工作。Listing3-3Installingaqueuecleanupfunction
voidmyFinalizerFunction(void*context) |
{ |
MyDataContext*theData=(MyDataContext*)context; |
//Cleanupthecontentsofthestructure |
myCleanUpDataContextFunction(theData); |
//Nowreleasethestructureitself. |
free(theData); |
} |
dispatch_queue_tcreateMyQueue() |
{ |
MyDataContext*data=(MyDataContext*)malloc(sizeof(MyDataContext)); |
myInitializeDataContextFunction(data); |
//Createthequeueandsetthecontextdata. |
dispatch_queue_tserialQueue=dispatch_queue_create("com.example.CriticalTaskQueue",NULL); |
if(serialQueue) |
{ |
dispatch_set_context(serialQueue,data); |
dispatch_set_finalizer_f(serialQueue,&myFinalizerFunction); |
} |
returnserialQueue; |
} |
AddingTaskstoaQueue
Important:Youshouldnevercallthedispatch_syncor
dispatch_sync_ffunctionfromataskthatisexecutinginthesamequeuethatyouareplanningtopasstothefunction.Thisisparticularlyimportantforserialqueues,whichareguaranteedtodeadlock,butshouldalsobeavoidedforconcurrentqueues.
dispatch_sync_f,而且传递给dispatch_sync和
dispatch_sync_f的queue参数和这个任务的queue是同一个。
dispatch_queue_tmyCustomQueue; |
myCustomQueue=dispatch_queue_create("com.example.MyCustomQueue",NULL); |
dispatch_async(myCustomQueue,^{ |
printf("Dosomeworkhere.\n"); |
}); |
printf("Thefirstblockmayormaynothaverun.\n"); |
dispatch_sync(myCustomQueue,^{ |
printf("Dosomemoreworkhere.\n"); |
}); |
printf("Bothblockshavecompleted.\n"); |
PerformingaCompletionBlockWhenaTaskIsDone
被dispatch到queue的任务是与创建它的代码是独立运行的。很多时候,需要在任务执行完成后,执行一个回调。在DQ中,可以使用completionblock去取代传统的回调方法。completionblock就是需要dispatch到queue中的一段代码。voidaverage_async(int*data,size_tlen, |
dispatch_queue_tqueue,void(^block)(int)) |
{ |
//Retainthequeueprovidedbytheusertomake |
//sureitdoesnotdisappearbeforethecompletion |
//blockcanbecalled. |
dispatch_retain(queue); |
//Dotheworkonthedefaultconcurrentqueueandthen |
//calltheuser-providedblockwiththeresults. |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{ |
intavg=average(data,len); |
dispatch_async(queue,^{block(avg);}); |
//Releasetheuser-providedqueuewhendone |
dispatch_release(queue); |
}); |
} |
PerformingLoopIterationsConcurrently
对于需要执行循环并发的情况,可以使用或者dispatch_apply
dispatch_apply_f。这适用于执行顺序是不重要的情况。对于串行队列,使用这方法对性能是没有提升的。
Important:Likearegular
forloop,the
anddispatch_apply
dispatch_apply_ffunctionsdonotreturnuntilallloopiterationsarecomplete.Youshouldthereforebecarefulwhencallingthemfromcodethatisalreadyexecutingfromthecontextofaqueue.Ifthequeueyoupassasaparametertothefunctionisaserialqueueandisthesameoneexecutingthecurrentcode,callingthesefunctionswilldeadlockthequeue.
Becausetheyeffectivelyblockthecurrentthread,youshouldalsobecarefulwhencallingthesefunctionsfromyourmainthread,wheretheycouldpreventyoureventhandlingloopfromrespondingtoeventsinatimelymanner.Ifyourloopcoderequiresanoticeableamountofprocessingtime,youmightwanttocallthesefunctionsfromadifferentthread.
和普通for循环一样,dispatch_apply和dispatch_apply_f会在所有的循环结束之后才会返回。所以,如果传递给dispatch_apply和dispatch_apply_f的queue参数是当前任务运行着的queue,而且这个queue是一个串行queue,那么这个调用这个方法就会导致queue死锁。
UsingObjective-CObjectsinYourTasks
每一个dispatchqueue持有自己的的autoreleasepool。
UsingDispatchSemaphorestoRegulatetheUseofFiniteResources
在访问一些有限的资源时,就需要用到dispatchsemaphores去控制这些任务的数量。dispatchsemaphores的好处时减少内核调用,只有在资源不足时才会内核调用并停止线程,知道信号量被标记。使用dispatchsemaphores的顺序:
Whenyoucreatethesemaphore(usingthe
function),youcanspecifyapositiveintegerindicatingthenumberofresourcesavailable.dispatch_semaphore_create
Ineachtask,call
towaitonthesemaphore.dispatch_semaphore_wait
Whenthewaitcallreturns,acquiretheresourceanddoyourwork.
Whenyouaredonewiththeresource,releaseitandsignalthesemaphorebycallingthe
function.dispatch_semaphore_signal
//Createthesemaphore,specifyingtheinitialpoolsize |
dispatch_semaphore_tfd_sema=dispatch_semaphore_create(getdtablesize()/2); |
//Waitforafreefiledescriptor |
dispatch_semaphore_wait(fd_sema,DISPATCH_TIME_FOREVER); |
fd=open("/etc/services",O_RDONLY); |
//Releasethefiledescriptorwhendone |
close(fd); |
dispatch_semaphore_signal(fd_sema); |
dispatch_semaphore_wait会时时资源数减1,如果为负数,通知内核阻塞线程。
WaitingonGroupsofQueuedTasks
dispatchgroup的作用时在某些任何执行完成之前阻塞线程。
dispatch_queue_tqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); |
dispatch_group_tgroup=dispatch_group_create(); |
//Addatasktothegroup |
dispatch_group_async(group,queue,^{ |
//Someasynchronouswork |
}); |
//Dosomeotherworkwhilethetasksexecute. |
//Whenyoucannotmakeanymoreforwardprogress, |
//waitonthegrouptoblockthecurrentthread. |
dispatch_group_wait(group,DISPATCH_TIME_FOREVER); |
//Releasethegroupwhenitisnolongerneeded. |
dispatch_release(group); |
DispatchQueuesandThreadSafety
对于线程安全的一些Tips:
Dispatchqueuesthemselvesarethreadsafe.Inotherwords,youcansubmittaskstoadispatchqueuefromanythreadonthesystemwithoutfirsttakingalockorsynchronizingaccesstothequeue.
Donotcallthe
functionfromataskthatisexecutingonthesamequeuethatyoupasstoyourfunctioncall.Doingsowilldeadlockthequeue.Ifyouneedtodispatchtothecurrentqueue,dosoasynchronouslyusingthedispatch_sync
function.dispatch_async
Avoidtakinglocksfromthetasksyousubmittoadispatchqueue.Althoughitissafetouselocksfromyourtasks,whenyouacquirethelock,youriskblockingaserialqueueentirelyifthatlockisunavailable.Similarly,forconcurrentqueues,waitingonalockmightpreventothertasksfromexecutinginstead.Ifyouneedtosynchronizepartsofyourcode,useaserialdispatchqueueinsteadofalock.
Althoughyoucanobtaininformationabouttheunderlyingthreadrunningatask,itisbettertoavoiddoingso.Formoreinformationaboutthecompatibilityofdispatchqueueswiththreads,see
DispatchSources
与底层系统交互往往需要耗费大量的时间。内核调用或者其他系统底层调用会导致上下文的改变,这会比在自己进程中的调用有更多的开销。所以很多系统库会提供异步接口去允许程序向系统异步提交请求。
GCD使用blcok和DQ向系统提交请求并得到相应的结果返回。
AboutDispatchSources
dispatchsources是协调系统底层事件处理的基础数据结构。GCD支持以下的类型的dispatchsources:Timerdispatchsourcesgenerateperiodicnotifications.
SignaldispatchsourcesnotifyyouwhenaUNIXsignalarrives.
Descriptorsourcesnotifyyouofvariousfile-andsocket-basedoperations,suchas:
Whendataisavailableforreading
Whenitispossibletowritedata
Whenfilesaredeleted,moved,orrenamedinthefilesystem
Whenfilemetainformationchanges
Processdispatchsourcesnotifyyouofprocess-relatedevents,suchas:
Whenaprocessexits
Whenaprocessissuesa
forkor
exectypeofcall
Whenasignalisdeliveredtotheprocess
MachportdispatchsourcesnotifyyouofMach-relatedevents.
Customdispatchsourcesareonesyoudefineandtriggeryourself.
dispatchsource取代异步回调方法,实现响应相关的系统事件。配置dispatchsource时,需要指定监听的事件,DQ和用于处理事件的代码。当关心的事件发生时,dispatchsource会向指定的DQ提交block或者函数。
dispatchsource会retain相关联的DQ防止其被释放。
为防止DQ事件积压,dispatchsources实现了事件合并方案。如果在旧事件没有被处理或者执行前,新事件到达了,dispatchsource会合并两个事件的数据。根据事件类型,可能会取代旧事件或者更新旧事件中的数据。
CreatingDispatchSources
创建DS的步骤:Createthedispatchsourceusingthe
function.dispatch_source_create
Configurethedispatchsource:
Assignaneventhandlertothedispatchsource;see
Fortimersources,setthetimerinformationusingthe
function;seedispatch_source_set_timer
Optionallyassignacancellationhandlertothedispatchsource;see
Callthe
functiontostartprocessingevents;seedispatch_resume
WritingandInstallinganEventHandler
使用或者dispatch_source_set_event_handler
两种类型的handler:
//Block-basedeventhandler |
void(^dispatch_block_t)(void) |
//Function-basedeventhandler |
void(*dispatch_function_t)(void*) |
dispatch_source_tsource=dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, |
myDescriptor,0,myQueue); |
dispatch_source_set_event_handler(source,^{ |
//Getsomedatafromthesourcevariable,whichiscaptured |
//fromtheparentcontext. |
size_testimated=dispatch_source_get_data(source); |
//Continuereadingthedescriptor... |
}); |
dispatch_resume(source); |
以下方法,可以获取事件的信息:
Function | Description |
---|---|
| Thisfunctionreturnstheunderlyingsystemdatatypethatthedispatchsourcemanages. Foradescriptordispatchsource,thisfunctionreturnsan inttypecontainingthedescriptorassociatedwiththedispatchsource. Forasignaldispatchsource,thisfunctionreturnsan inttypecontainingthesignalnumberforthemostrecentevent. Foraprocessdispatchsource,thisfunctionreturnsa pid_tdatastructurefortheprocessbeingmonitored. ForaMachportdispatchsource,thisfunctionreturnsa mach_port_tdatastructure. Forotherdispatchsources,thevaluereturnedbythisfunctionisundefined. |
| Thisfunctionreturnsanypendingdataassociatedwiththeevent. Foradescriptordispatchsourcethatreadsdatafromafile,thisfunctionreturnsthenumberofbytesavailableforreading. Foradescriptordispatchsourcethatwritesdatatoafile,thisfunctionreturnsapositiveintegerifspaceisavailableforwriting. Foradescriptordispatchsourcethatmonitorsfilesystemactivity,thisfunctionreturnsaconstantindicatingthetypeofeventthatoccurred.Foralistofconstants,seethe enumeratedtype. Foraprocessdispatchsource,thisfunctionreturnsaconstantindicatingthetypeofeventthatoccurred.Foralistofconstants,seethe enumeratedtype. ForaMachportdispatchsource,thisfunctionreturnsaconstantindicatingthetypeofeventthatoccurred.Foralistofconstants,seethe dispatch_source_machport_flags_tenumeratedtype. Foracustomdispatchsource,thisfunctionreturnsthenewdatavaluecreatedfromtheexistingdataandthenewdatapassedtothe function. |
| Thisfunctionreturnstheeventflagsthatwereusedtocreatethedispatchsource. Foraprocessdispatchsource,thisfunctionreturnsamaskoftheeventsthatthedispatchsourcereceives.Foralistofconstants,seethe enumeratedtype. ForaMachportdispatchsourcewithsendrights,thisfunctionreturnsamaskofthedesiredevents.Foralistofconstants,seethe dispatch_source_mach_send_flags_tenumeratedtype. ForacustomORdispatchsource,thisfunctionreturnsthemaskusedtomergethedatavalues. |
InstallingaCancellationHandler
Cancellationhandler的作用是用于在DS被release前清楚资源。多少类型的DS,Cancellationhandler是可选实现的,只有在对需要被更新的DS执行一些自定义行为时需要到。对于使用descriptor或者Machport的DS而言,必须提供Cancellationhandler去关闭它们。可以通过
ordispatch_source_set_cancel_handler
设置cancellationhandler。dispatch_source_set_cancel_handler_f
ChangingtheTargetQueue
可以在任何时候使用函数去修改创建DS时指定的queue。在需要改变DS的事件的优先级的时候,需要做这个操作。dispatch_set_target_queue
这个操作时异步的,并不能保证即时执行。对于已经出列的在等待处理的事件handler,它会继续在前一个queue里面执行。
AssociatingCustomDatawithaDispatchSource
可以使用CancelingaDispatchSource
DS一直处于活动状态知道显式调用函数,cancel后还要清除掉它。dispatch_source_cancel
voidRemoveDispatchSource(dispatch_source_tmySource) |
{ |
dispatch_source_cancel(mySource); |
dispatch_release(mySource); |
} |
SuspendingandResumingDispatchSources
可以使用dispatch_suspend和
dispatch_resume去暂停和重启DS。
相关文章推荐
- iOS 系统框架分层结构
- iOS app的webview注入JS遇到的坑
- iOS开发代码编写规范
- iOS压缩与解压框架SSZipArchive使用详解
- iOS开发中通过蓝牙连接蓝牙打印机设备进行小票打印
- iOS开发之自定义手势
- Xcode创建通用动态库 (详解)
- iOS之代理设计模式
- iOS代码拾遗录-AVAudioSession状态
- iOS优秀技术博客
- iOS--NSDictionary字典的初始化增删
- ios开发 各种字体
- iOS 自有地图
- 13位时间戳转化北京时间
- iOS开发之原生二维码生成与扫描
- iOS 获取设备相关信息
- IOS单选框的实现
- iOS 开发技巧-制作环形进度条
- iOS开发之自定义表情键盘(组件封装与自动布局)
- iOS开发笔记--先弄清楚这里的学问,再来谈iOS内存管理与优化(二)