第一种方法是通过GCD的API生成Dispatch Queue。
通过dispatch_queue_create函数可生成Dispatch Queue。以下源代码生成了Serial Dispatch Queue。
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create(“com.example.gcd.MySerialDispatchQueue”, NULL);
在说明dispatch_queue_create函数之前,先讲一下关于Serial Dispatch Queue生成个数的注意事项。
Concurrent Dispatch Queue并行执行多个追加处理,而Serial Dispatch Queue同时只能执行一个追加处理。虽然Serial Dispatch Queue和Concurrent Dispatch Queue受到系统资源的限制,但用dispatch_queue_create函数可生成任意多个Dispatch Queue。
当生成多个Serial Dispatch Queue时,各个Serial Dispatch Queue将并行执行。虽然在一个Serial Dispatch Queue中同时只能执行一个追加处理,但如果将处理分别追加到4个Serial Dispatch Queue中,各个Serial Dispatch Queue执行一个,即为同时执行4个处理。
以上是关于Serial Dispatch Queue生成个数注意事项的说明。一旦生成Serial Dispatch Queue并追加处理,系统对于一个Serial Dispatch Queue就只生成并使用一个线程。如果生成10000个Serial Dispatch Queue,那么就生成10000个线程。
如果过多使用线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。
只在为了避免多线程问题之一——多个线程更新相同资源数据竞争时使用Serial Dispatch Queue。
但是Serial Dispatch Queue的生成个数应当仅限所必需的数量。例如更新数据库时1个表生成1个Serial Dispatch Queue,更新文件时1个文件或是可以分割的1个文件块生成1个Serial Dispatch Queue。虽然“Serial Dispatch Queue比Concurrent Dispatch Queue能生成更多的线程”,但绝不能激动之下大量生成Serial Dispatch Queue。
当想并行执行不发生数据竞争等问题的处理时,使用Concurrent Dispatch Queue。而且对于Concurrent Dispatch Queue来说,不管生成多少,由于XNU内核只使用有效管理的线程,因此不会发生Serial Dispatch Queue的那些问题。
下面我们回来继续讲dispatch_queue_create函数。该函数的第一个参数指定Serial Dispatch Queue的名称。像此源代码这样,Dispatch Queue的名称推荐使用应用程序ID这种逆序全程域名。该名称在Xcode和Instruments的调试器中作为Dispatch Queue名称表示。另外,该名称也应用程序崩溃时所生成的Crash Log中。所以命名时应遵循这样的原则:对我们编程人员来说简单易懂,对用户来说也要易懂。如果嫌命名麻烦设为NULL也可以,但你在调试中一定会后悔没有为Dispatch Queue署名。
生成Serial Dispatch Queue时,像该源代码这样,将第二个参数指定为NULL。生成Concurrent Dispatch Queue时,像下面源代码一样,指定为DISPATCH_QUEUE_CONCURRENT。
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(“com.example.gcd.myConcurrentDispatchQueue”, DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t函数的返回值表示为Dispatch Queue的“dispatch_queue_t类型”。在之前源代码中所出现的变量queue均为dispatch_queue_t类型变量。
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(“com.example.gcd.MyConcurrentDispatchQueue”, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentDispatchQueue, ^{ NSLog(@“block on myConcurrentDispatchQueue”);});
该源代码在Concurrent Dispatch Queue中执行指定的Block。
另外,尽管有ARC这一通过编译器自动管理内存的优秀技术,但生成的Dispatch Queue必须由程序员负责释放。这是因为Dispatch Queue并没有像Block那样具有作为Objective-C对象来处理的技术。
通过dispatch_queue_create函数生成的Dispatch Queue在使用结束后通过dispatch_release函数释放。
dispatch_release(mySerialDispatchQueue);
该名称中含有release,由此可以推测出相应地也存在dispatch_retain函数。
dispatch_retain(mySerialDispatchQueue);
即Dispatch Queue也像Objective-C的引用计数式内存管理一样,需要通过dispatch_retain函数和dispatch_release函数的引用计数来管理内存。在前面的源代码中,需要释放通过dispatch_queue_create函数生成并赋值给变量myConcurrentDispatchQueue中的Concurrent Dispatch Queue。
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(“com.example.gcd.MyConcurrentDispatchQueue”, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{NSLog(@”block on myConcurrentDispatchQueue”);});
dispatch_release(myConcurrentDispatchQueue);
虽然Concurrent Dispatch Queue是使用多线程执行追加的处理,但像该例这样,在dispatch_async函数中追加Block到Concurrent Dispatch Queue,并立即通过dispatch_create函数进行释放是否可以呢?
该源代码完全没有问题,在dispatch_async函数中追加Block到Dispatch Queue,换言这,该Block通过dispatch_retain函数持有Dispatch Queue。无论Dispatch Queue是Serial Dispatch Queue还是Concurrent Dispatch Queue都一样。一旦Block执行结束,就通过dispatch_release函数释放该Block持有的Dispatch Queue。
也就是说,在dispatch_async函数中追加Block到Dispatch Queue后,即使立即释放Dispatch Queue,该Dispatch Queue由于被Block所持有也不会被废弃,因而Block能够执行。Block执行结束后会释放Dispatch Queue,这时谁都不持有Dispatch Queue,因此它会被废弃。
另外,能够使用dispatch_retain函数和dispatch_release函数的地方不仅是在Dispatch Queue。在之后介绍的几个GCD的API中,名称中含有“create”的API在不需要其生成的对象时,有必要通过dispatch_release函数进行释放。在通过函数或方法获取Dispatch Queue以及其他名称中含有create的API生成的对象时,有必要通过dispatch_retain函数持有,并在不需要时通过dispatch_release函数释放。