研究GCD有一段时间,翻译了多篇文章,找了不少的资料,看了不少官方文档,看起来很难,实际上很简单,本人一一进行讲解怎么使用.多线程
支持ARC以及非ARC,不管在ARC环境仍是在非ARC环境,都须要调用dispatchRelease方法来释放init出的GCDGroup,GCDQueue,GCDSemaphore,以及GCDTimer.并发
1. 系统并发线程队列app
[[GCDQueue globalQueue] execute:^{ // 在系统默认级别的线程队列中执行并发的操做 }];
其实是在这个线程队列中dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)执行操做,官方文档以下less
The well-known global concurrent queues cannot be modified. Calls to dispatch_suspend, dispatch_resume, dispatch_set_context, and the like have no effect when used with queues returned by this function.
Blocks submitted to these global concurrent queues may be executed concurrently with respect to each other.异步
这个妇孺皆知的的并发队列是不能更改的,因此,你调用方法dispatch_suspend,dispatch_resume以及dispatch_set_context等等,都是没有效果的.被提交到这个线程队列的block将会被并发的执行,不存在前后顺序.async
请看如上结果,执行没有前后顺序,是同时执行的.post
2. 系统串行线程队列测试
[[GCDQueue mainQueue] execute:^{ // 在系统主线程队列中执行串行操做 }];
The main queue is automatically created by the system and associated with your application’s main thread.this
主线程队列是被系统自动建立的,用来关联上你的应用的主线程.spa
As with the global concurrent queues, calls to dispatch_suspend, dispatch_resume, dispatch_set_context, and the like have no effect when used with queues returned by this function.
做为全局的并发队列,调用dispatch_suspend,dispatch_resume,dispatch_set_context相似的方法都将无效.
请看如上结果,执行有前后顺序.
问:既然有前后顺序,也有多是先执行了第一个block,再执行了第二个block...,只是时间很短,打印出有顺序而已,也有多是一块儿执行的,对吧?
先看看官方dispatch_async方法的文档:
This function is the fundamental mechanism for submitting blocks to a dispatch queue. Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. The target queue determines whether the block is invoked serially or concurrently with respect to other blocks submitted to that same queue. Independent serial queues are processed concurrently with respect to each other.
这是个基础的提交block到一个指定queue的方法.只要提交了这个block,这个方法就会当即返回,根本就不会等待看这个block是否被调用了.这个指定的queue的类型决定了这个block是不是串行的执行抑或是并发的执行.独立的串行队列将会对每个block按照顺序执行并发操做.
我测试了下系统主线程队列的执行状况,代码以下,结论是:必须是上一个串行的block执行完毕后才会执行下一个block.
[[GCDQueue mainQueue] execute:^{ NSLog(@"1"); for (int i = 0; i < 1000000000; i++) { static int flag = 1; flag++; } NSLog(@"kkkkkk"); }]; [[GCDQueue mainQueue] execute:^{ NSLog(@"2"); }]; [[GCDQueue mainQueue] execute:^{ NSLog(@"3"); }]; [[GCDQueue mainQueue] execute:^{ NSLog(@"4"); }]; [[GCDQueue mainQueue] execute:^{ NSLog(@"5"); }];
再测试了下本身建立的串行队列
GCDQueue *queue = [[GCDQueue alloc] initSerial]; [queue execute:^{ NSLog(@"1"); for (int i = 0; i < 1000000000; i++) { static int flag = 1; flag++; } NSLog(@"kkkkkk"); }]; [queue execute:^{ NSLog(@"2"); }]; [queue execute:^{ NSLog(@"3"); }]; [queue execute:^{ NSLog(@"4"); }]; [queue execute:^{ NSLog(@"5"); }];
至少从打印结果处得知,是按照序列来执行block的,只有上一个block执行完毕了才会执行下一个block,认识这一点很重要哦.
[[GCDQueue globalQueue] execute:^{ // 并发线程执行阻塞操做 [[GCDQueue mainQueue] execute:^{ // 主线程更新UI }]; }];
是最经常使用的一种方式,也是你们见得最多的方式.
3. 在某个指定的队列中执行延时操做
[[GCDQueue globalQueue] execute:^{ // 延时3秒后执行操做 } afterDelay:3 * NSEC_PER_SEC];
-dispatch_after-
Enqueue a block for execution at the specified time.
This function waits until the specified time and then asynchronously adds block to the specified queue.
将一个要执行的并设定了时间block插入队列.
这个方法会等到指定的时间,异步的将block添加到指定的队列中.
从如下代码中的执行结果能够看出,即便提交了任务,若是出现了死循环,后面提交的任务也是没法执行的.
[[GCDQueue mainQueue] execute:^{ NSLog(@"1"); while (1) { } } afterDelay:3 * NSEC_PER_SEC]; [[GCDQueue mainQueue] execute:^{ NSLog(@"2"); } afterDelay:5 * NSEC_PER_SEC];
下面的这个例子,就能看出,只要一个提交的任务可以在时间点上完成,是不会影响后面的任务执行的,可是若是把sleep(1)改为sleep(3),就会影响后面提交的任务执行.
[[GCDQueue mainQueue] execute:^{ NSLog(@"1"); sleep(1); } afterDelay:3 * NSEC_PER_SEC]; [[GCDQueue mainQueue] execute:^{ NSLog(@"2"); } afterDelay:5 * NSEC_PER_SEC];
4. 在group中监听某些线程完成了,以后再执行某个线程
-dispatch_group_notify-
This function schedules a notification block to be submitted to the specified queue when all blocks associated with the dispatch group have completed. If the group is empty (no block objects are associated with the dispatch group), the notification block object is submitted immediately.
这个方法安排了一个通知用的block到这个指定的queue当中,而当全部与这个group相关联的block都执行完毕了,才会执行这个通知的block.若是这个组空了,那这个通知用的block就会被当即的执行.
另外一种监听线程执行完毕的方式,不过可以设定超时时间
GCDGroup *group = [GCDGroup new]; [group enter]; [[GCDQueue globalQueue] execute:^{ [group leave]; }]; [group enter]; [[GCDQueue globalQueue] execute:^{ [group leave]; }]; [[GCDQueue globalQueue] execute:^{ [group wait:3 * NSEC_PER_SEC]; // 若是超时了3秒,上面的线程还没执行完,就跳过了 }];
请注意,enter与leave必须成对出现!
-dispatch_group_enter-
Explicitly indicates that a block has entered the group.
精确指定一个block进入了group.
Calling this function increments the current count of outstanding tasks in the group. Using this function (with dispatch_group_leave) allows your application to properly manage the task reference count if it explicitly adds and removes tasks from the group by a means other than using the dispatch_group_async function. A call to this function must be balanced with a call to dispatch_group_leave. You can use this function to associate a block with more than one group at the same time.
调用这个方法,将会增长当前在组中未解决的任务的数量.使用这个方法是为了可以管理任务的细节,指定何时结束这个任务,你也可使用dispatch_group_async这个方法.调用了这个方法就必须使用dispatch_group_leave来平衡.你能够同时的给一个block关联上不一样的group.
dispatch_group_enter 与 dispatch_group_leave 可以处理更加复杂的任务类型,推荐!
5. 使用定时器
GCDTimer *timer = [[GCDTimer alloc] initInQueue:[GCDQueue globalQueue]]; [timer event:^{ // 每1秒执行一次你的event } timeInterval:1 * NSEC_PER_SEC]; [timer start];
此定时器不能暂停,只能销毁后释放掉对象.
[timer destroy]; [timer dispatchRelease];
6. 使用信号量
GCDSemaphore *sem = [[GCDSemaphore alloc] init]; GCDTimer *timer = [[GCDTimer alloc] initInQueue:[GCDQueue globalQueue]]; [timer event:^{ [sem signal]; } timeInterval:NSEC_PER_SEC]; [timer start]; [[GCDQueue globalQueue] execute:^{ while (1) { [sem wait]; NSLog(@"Y.X."); } }];
一个发送信号,一个接受信号
-dispatch_semaphore_signal-
Signals (increments) a semaphore.
Increment the counting semaphore. If the previous value was less than zero, this function wakes a thread currently waiting in dispatch_semaphore_wait.
This function returns non-zero if a thread is woken. Otherwise, zero is returned.
发送信号增长一个信号量.
增长一个信号量,若是当前值小于或者等于0,这个方法会唤醒某个使用了dispatch_semaphore_wait的线程.
若是这个线程已经唤醒了,将会返回非0值,不然返回0
基本已经讲解完了GCD的部分功能,我会在附录中讲解GCDQueue类中下面列举的方法如何使用以及有什么含义,这些方法涉及到了多线程同步问题哦,比基础用法有着更多的用处,更高级.
waitExecute:
barrierExecute:
waitBarrierExecute:
suspend
resume