GCD 多线程API编程笔记.docx
- 文档编号:23340686
- 上传时间:2023-05-16
- 格式:DOCX
- 页数:31
- 大小:30.36KB
GCD 多线程API编程笔记.docx
《GCD 多线程API编程笔记.docx》由会员分享,可在线阅读,更多相关《GCD 多线程API编程笔记.docx(31页珍藏版)》请在冰豆网上搜索。
GCD多线程API编程笔记
GCD多线程API编程笔记
GCD是iOS编程中实现多线程的常用API,使用方便,无须进行线程的管理,由系统代劳。
同时GCD基于C实现,性能强。
在名著《PromultithreadingandmemorymanagementforiOSandOSX》(中文名:
《Objective-C高级编程iOS与OSX多线程和内存管理》)中,专门有章节讲解,特根据此书讲解做些笔记。
一概要
GCD是GrandCentralDispatch(GCD)的缩写,是异步执行任务的技术之一。
一般将应用程序中记述的线程管理用的代码在系统级中实现。
开发者只需定义想执行的任务,然后加入适当的DispatchQueue中,GCD就能生成相应的线程并计划执行任务。
由于线程管理是系统来实现的,因此可以统一管理,也可执行任务,这样就比之前的线程更有效率。
二API详解
1.DispatchQueue
“DispatchQueue”是执行处理的等待队列。
应用程序编程通过dispatch_async函数等API,在Block语法中记述想执行的处理,并将其追加到DispatchQueue中。
DispatchQueue按照追加的顺序(先进先出FIFO,FirstInFirsOut)执行处理。
根据处理的策略,分为两种DispatchQueue,一种是等待现在执行中处理的SerialDispatchQueue;另外一种是不等待现在执行任务队列处理的ConcurrentDispatchQueue。
比较DispatchQueue的种类如下:
DispatchQueue的种类
种类名称…………………………执行情况
SerialDispatchQueue…………等待现在执行中处理结束
ConcurrentDispatchQueue…..不等待现在执行处理结束
准备以下源代码,在dispatch_async中追加多个处理。
//Democode1
dispatch_async(queue,blk0);
dispatch_async(queue,blk1);
dispatch_async(queue,blk2);
dispatch_async(queue,blk3);
dispatch_async(queue,blk4);
dispatch_async(queue,blk5);
dispatch_async(queue,blk6);
dispatch_async(queue,blk7);
当变量queue为SerialDispatchQueue时,因为要等待现在执行中的处理结束,所以首先执行任务blk0,待blk0执行结束后,接着执行blk1,blk1结束后再开始执行blk2,如此重复。
同时执行的任务只能为1个。
即执行源代码片段Democode1后,按照以下顺序进行处理。
blk0
blk1
blk2
blk3
blk4
blk5
blk6
blk7
但当变量queue为ConcurrentDispatchQueue时,因为不用等待现在执行中的处理结束,所以执行顺序将变成:
首先执行blk0,不管blk0的执行是否结束,都开始执行后面的blk1,不管blk1的执行是否结束,都开始执行后面的blk2,如此重复循环。
这样虽然不用等待处理结束,可以并行执行多个处理,但并行执行的处理数量取决于当前系统的状态。
即iOS和OSX基于DispatchQueue中的处理数、CPU核数及CPU负荷等当前系统的状态来决定ConcurrentDispatchQueue中并行执行的处理数。
所谓“并行执行”,就是使用多个线程同时执行多个处理。
总结来说,SerialDispatchQueue使用一个线程;而ConcurrentDispatchQueue使用的是多个线程。
iOS和OSX的核心–XNU内核决定应当使用的线程数,并只生成所需的线程执行处理。
另外,当处理结束,应当执行的处理数减少时,XNU内核会结束不再需要的线程。
XNU内核仅使用ConcurrentDispatchQueue便可完美地管理并行执行多个处理的线程。
假设准备4个ConcurrentDispatchQueue使用的线程。
首先:
blk0在线程0中开始执行,接着blk1在线程1中、blk2在线程2中、blk3在线程3中开始执行。
线程0中blk0执行结束后,开始执行blk4,由于线程1中blk1的执行没有结束,因此线程数中blk2执行结束后开始执行blk5,就这样循环往复。
ConcurrentDispatchQueue执行示例
thread0_thread1_hread2_hread3
blk0====blk1====blk2====blk3
blk4====blk5
blk7
像这样在ConcurrentDispatchQueue中执行处理时,执行顺序会根据处理内容和系统状态发生改变。
它不同于执行顺序固定的SerialDispatchQueue。
在不能改变执行的处理顺序或不想并行执行多个处理时使用SerialDispatchQueue。
那么如何得到这两种Queue呢?
方法有两种。
2.dispatch_queue_create
第一种方式是使用GCD的API生成DispatchQueue.
通过dispatch_queue_create函数可生成DispatchQueue。
以下源代码生成了SerialDispatchQueue。
dispatch_queuetmySerialDispatchQueue=dispatch_queue_create("com.example.gcd.MySerialDispatchQueue",NULL);
根据苹果的API函数声明:
/*!
*@functiondispatch_queue_create
*
*@abstract
*Createsanewdispatchqueuetowhichblocksmaybesubmitted.
*
*@discussion
*DispatchqueuescreatedwiththeDISPATCH_QUEUE_SERIALoraNULLattribute
*invokeblocksseriallyinFIFOorder.
*
*DispatchqueuescreatedwiththeDISPATCH_QUEUE_CONCURRENTattributemay
*invokeblocksconcurrently(similarlytotheglobalconcurrentqueues,but
*potentiallywithmoreoverhead),andsupportbarrierblockssubmittedwith
*thedispatchbarrierAPI,whiche.g.enablestheimplementationofefficient
*reader-writerschemes.
*
*Whenadispatchqueueisnolongerneeded,itshouldbereleasedwith
*dispatch_release().Notethatanypendingblockssubmittedtoaqueuewill
*holdareferencetothatqueue.Thereforeaqueuewillnotbedeallocated
*untilallpendingblockshavefinished.
*
*Passingtheresultofthedispatch_queue_attr_make_with_qos_class()function
*totheattrparameterofthisfunctionallowsaqualityofserviceclassand
*relativeprioritytobespecifiedforthenewlycreatedqueue.
*Thequalityofserviceclasssospecifiedtakesprecedenceoverthequality
*ofserviceclassofthenewlycreateddispatchqueue'stargetqueue(ifany)
*aslongthatdoesnotresultinalowerQOSclassandrelativepriority.
*
*Whennoqualityofserviceclassisspecified,thetargetqueueofanewly
*createddispatchqueueisthedefaultpriorityglobalconcurrentqueue.
*
*@paramlabel
*Astringlabeltoattachtothequeue.
*ThisparameterisoptionalandmaybeNULL.
*
*@paramattr
*DISPATCH_QUEUE_SERIAL,DISPATCH_QUEUE_CONCURRENT,ortheresultofacallto
*thefunctiondispatch_queue_attr_make_with_qos_class().
*
*@result
*Thenewlycreateddispatchqueue.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORTDISPATCH_MALLOCDISPATCH_RETURNS_RETAINEDDISPATCH_WARN_RESULT
DISPATCH_NOTHROW
dispatch_queue_t
dispatch_queue_create(constchar*label,dispatch_queue_attr_tattr);
dispatch_queue_create函数的第一个参数是表示队列名;第二个参数表示队列的种类,声明为NULL表示串行队列,与参数DISPATCH_QUEUE_SERIAL同样的效果表示串行队列。
关于SerialDispatchQueue生成个数的注意事项如下:
SerialDispatchQueue同时只能执行1个追加处理。
虽然SerialDispatchQueue和ConcurrentDispatchQueue受到系统资源的限制,但用dispatch_queue_create函数可生成任意多个DispatchQueue。
当生成多个SerialDispatchQueue时,在每个SerialDispatchQueue中,同时只能执行一个追加处理,但各个SerialDispatchQueue将并行执行,达到同时执行多个任务处理的效果。
一旦生成SerialDispatchQueue并追加处理,系统对于一个SerialDispatchQueue就只生成并使用一个线程。
如果生成2000个SerialDispatchQueue,那么就生成2000个线程。
但是如果使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。
只在避免多线程编程问题之一—多个线程更新相同资源导致数据竞争时使用SerialDispatchQueue。
但SerialDispatchQueue的生成个数应当仅限所必需的数量。
例如更新数据库时1个表生成1个SerialDispatchQueue,更新文件时1个文件或是可以分割的1个文件块生成1个SerialDispatchQueue.虽然“SerialDispatchQueue比ConcurrentDispatchQueue能生成更多的线程”,但绝不能激动之下大量生成SerialDispatchQueue。
当想并行执行不发生数据竞争等问题的处理时,使用ConcurrentDispatchQueue。
而且对于ConcurrentDispatchQueue来说,不管生成多少,由于XNU内核只使用有效管理的线程,因此不会发生SerialDispatchQueue的那些问题。
继续讲dispatch_queue_create函数。
该函数的第一个参数指定SerialDispatchQueue的名称。
像此源代码这样,DispatchQueue的名称推荐使用应用程序ID这种逆序全程域名(FQDN,fullyqualifieddomainname)。
该名称在XCode和Instruments的调试器中作为DispatchQueue名称来表示。
另外,该名称也会出现在应用程序崩溃时所生成的crashLog中。
我们命名时应遵循这样的原则:
对编程人员和用户来说都要易懂。
如果嫌命名麻烦设为NULL也可以,但在调试中一定会后悔没有为DispatchQueue署名。
生成SerialDispatchQueue时,像该源代码这样,将第二个参数指定为NULL。
生成ConcurrentDispatchQueue时,像下面源代码一样,指定为DISPATCH_QUEUE_CONCURRENT。
dispatch_queue_tmyConcurrentDispatchQueue=dispatch_queue_create("com.example.gcd.MyConcurrentDispatchQueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentDispatchQueue,
^{NSLog(@"blockonmyConcurrentDispatchQueue");});
并在生成的ConcurrentDispatchQueue中执行指定的Block。
另外,遗憾的是尽管有ARC这一通过编译器自动管理内存的优秀技术,但生成的DispatchQueue必须由程序员负责释放。
这是因为DispatchQueue并没有像Block那样具有作为Objective-C对象来处理的技术。
通过dispatch_queue_create函数生成的DispatchQueue在使用结束后通过dispatch_release函数释放。
dispatch_release(mySerialDispatchQueue);
相应地,也存在dispatch_retain函数。
dispatch_retain(myConcurrentDispatchQueue);
即DispatchQueue也像Objective-C的饮用技术式内存管理一样,需要通过dispatch_retain函数和dispatch_release函数的引用计数来管理内存。
在前面的源代码中,需要释放通过dispatch_queue_create函数生成并赋值给变量myConcurrentDispatchQueue中的ConcurrentDispatchQueue。
再看一个例子:
dispatch_queue_tmyConcurrentDispatchQueue=dispatch_queue_create("com.demo.gcd.myConcurrentDispatchQueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentDispatchQueue,
^{NSLog(@"blockonmyConcurrentDispatchQueue");});
dispatch_release(myConcurrentDispatchQueue);
虽然ConcurrentDispatchQueue是使用多线程进行的追加处理,但像该例这样,在dispatch_async函数中追加Block到ConcurrentDispatchQueue,并立即通过dispatch_release函数进行释放是否可以呢?
该源代码完全没有问题。
在dispatch_async函数中追加Block到DispatchQueue,换言之:
该Block通过dispatch_retain函数持有DispatchQueue。
无论DispatchQueue是SerialDispatchQueue还是ConcurrentDispatchQueue都一样。
一旦Block执行结束,就通过dispatch_release函数释放该Block持有的DispatchQueue。
也就是说,在dispatch_async函数中追加Block到DispatchQueue后,即使立即释放DispatchQueue,该DispatchQueue由于被Block所持有也不会被废弃,因而Block能够执行。
Block执行结束后会释放DispatchQueue,这时谁都不持有DispatchQueue,因此它会被释放。
另外,能够使用dispatch_retain函数和dispatch_release函数的地方不仅是在DispatchQueue中。
在之后介绍的几个GCD的API中,名称中含有“create”的API在不需要其生成的对象时,有必要通过dispatch_release函数进行释放。
在通过函数或方法获取DispatchQueue以及其它名称中含有create的API生成的对象时,有必要通过dispatch_retain函数持有,并在不需要时通过dispatch_release函数释放。
3.MainDispatchQueue/GlobalDispatchQueue
第二种方法是获取系统标准提供的DispatchQueue。
实际上不用特意生成DispatchQueue系统也会给我们提供几个。
那就是MainDispatchQueue和GlobalDispatchQueue。
3.1MainDispatchQueue
主线程中执行的DispatchQueue,因为主线程只有一个,所以MainDispatchQueue自然就是SerialDispatchQueue。
追加到MainDispatchQueue中的任务在主线程的RunLoop中执行。
由于在主线程中执行,因此将用户界面更新等一些必须在主线程中执行的处理追加到MainDispatchQueue中使用。
这正好和NSObject类的performSelectorOnMainThread实例方法相同。
3.2GlobalDispatchQueue
所有程序都可以使用,没有必要通过dispatch_、queue_create函数逐个生成ConcurrentDispatchQueue。
只要获取GlobalDispatchQueue即可。
GlobalDispatchQueue具有4个优先级,分别是:
1)高优先级(HighPriority)
2)默认优先级(DefaultPriority)
3)低优先级(LowPriority)
4)后台优先级(BackgroundPriority).
通过XNU内核管理的用于GlobalDispatchQueue的线程,将各自使用的GlobalDispatchQueue的执行优先级作为线程的优先级来使用。
在向GlobalDispatchQueue追加处理时,应选择与处理内容执行优先级一致的GlobalDispatchQueue。
但是通过XNU内核用于GlobalDispatchQueue的线程并不能保证实时性,因此执行优先级只是大致的判断。
例如在处理内容的执行可有可无时,使用后台优先级的GlobalDispatchQueue等,只能进行这种程度的划分。
系统提供的DispatchQueue总结如下表:
DispatchQueue的种类
名称———————————————–种类————————————–说明
MainDispatchQueue————————SerialDispatchQueue————–主线程执行
GlobalDispatchQueue(HighPriority)—ConcurrentDispatchQueue——-执行优先级:
高
GlobalDispatchQueue(DefaultPriority)—ConcurrentDispatchQueue——-执行优先级:
默认
GlobalDispatchQueue(LowPriority)—ConcurrentDispatchQueue——-执行优先级:
低
GlobalDispatchQueue(BackgroundPriority)—ConcurrentDispatchQueue——-执行优先级:
后台
各种DispatchQueue的获取方法如下:
//MainDispatchQueue的获取方法
dispatch_queue_tmainDispatchQueue=dispatch_get_main_queue();
//GlobalDispatchQueue(高优先级)的获取方法
dispatch_queue_tglobalDispatchQueueHigh=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY,0);
//GlobalDispatchQueue(默认优先级)的获取方法
dispatch_queue_tglobalDispatchQueueDefault=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- GCD 多线程API编程笔记 多线程 API 编程 笔记