聊一聊GCD的那些事
GCD是iOS开发中经常使用到的技术,最近工作比较悠闲,有时间对GCD做一个全面的学习,这篇文章将对GCD做一个全面的解析。
目录
- 文字描述
- Dispatch Queue
- dispatch_sync&dispatch_async
- 创建的线程任务有四种执行方式
- Dispatch Group
- Dispatch Block
- dispatch_after
- dispatch_apply
- dispatch_once
- dispatch_barrier_async
- dispatch_set_target_queue
- dispatch_semaphore_t(信号量)
- GCD定时器
1. 文字描述
GCD英文全称:Grand Central Dispatch 翻译就是 宏大的中央调度,是苹果开发的一种支持并行操作的机制,基于C语言,提供了非常多强大的函数
在了解GCD并使用之前,必须要掌握四个名词:串行,并发,同步,异步
串行(Serial):
一个任务执行完, 再执行下一个任务
并发 (Concurrent):
多个任务同时执行(自动开启多个线程),只有在异步函数下才有效
同步(Synchronous):
在当前线程中执行任务,不具备开启新线程的能力
提交的任务在执行完成后才会返回
同步函数: dispatch_sync()
异步 (Asynchronous):
在新的线程中执行任务, 具备开启线程的能力
在新线程中执行任务,具备开启新线程的能力
提交的任务立刻返回,在后台队列中执行
异步函数: dispatch_async()
2.Dispatch Queue
Dispatch Queue是执行处理的等待队列, 按照先进先出(FIFO, First-In-First-Out)的顺序进行任务处理.
开发者将需要执行的任务添加到合适的Dispatch Queue中即可,Dispatch Queue会根据任务添加的顺序先到先执行,其中有以下几种队列:
另外, 队列分两种, 一种是串行队列(Serial Dispatch Queue), 一种是并行队列(Concurrent Dispatch Queue).
Dispatch Queue的种类 | 说明 |
---|---|
Serial Dispatch Queue | 等待现在执行中处理结束 |
Concurrent Dispatch Queue | 不等待现在执行中处理结束 |
//创建一个串行队列
dispatch_queue_t serialQueue=dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
//创建一个并发队列
dispatch_queue_t concurrentQueue=dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
//第一个参数为队列名,第二个参数为队列类型,当然,第二个参数人如果写NULL,创建出来的也是一个串行队列。然后我们在异步线程来执行这个队列:
另外系统为我们准备了两个队列
main dispatch queue
功能跟主线程一样,通过dispatch_get_main_queue()来获取,提交到main queue的任务实际上都是在主线程执行的,所以这是一个串行队列dispatch_queue_t queue = dispatch_get_main_queue();
global dispatch queues
系统给每个应用提供四个全局的并发队列,这四个队列分别有不同的优先级:高、默认、低以及后台,用户不能去创建全局队列,只能根据优先级去获取:dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3.dispatch_sync&dispatch_async
执行队列中任务的两种方式,dispatch_sync是同步任务,dispatch_async是异步任务
1.用同步的方式执行任务(同步:synchronization), 只能在当前线程中执行任务,不具备开启新线程的能力
/*
* 第一个参数:该任务所在的队列
* 第二个参数:该任务要做的事情
*/
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
假如我指定的队列A是串行队列,则该队列中只能有一个线程,也就是说我放在队列A中的任务,所以必须得一个一个的执行。不仅如此,在上面我们还手动选择了在队列A中用同步的方式执行任务,这也限制了,队列中的任务只能一个一个执行。
假如我指定的队列A是并行队列,则该队列中可以开辟多个线程去执行任务,虽然如此,但由于我们在上面手动选择了在队列A中用同步的方式执行线程,所以队列A中的任务也只能一个一个去执行,不能开辟多线程同时执行。
2.用异步的方式执行任务(异步:asynchronous),可以在新的线程中执行任务,具备开启新线程的能力。
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
- 假如此时我指定的队列B是并行队列,则表明该队列中可以存在多个线程,又因为我们采用的是异步的方式执行任务,所以在这个队列的任务可以实现同时运行。
- 假如此时我指定的队列B是串行队列,则表明该队列中,只能有一个线程,所以尽管我采用异步的方式执行任务,但该队列中的任务还是只能一个一个的运行。
4.创建的线程任务有四种执行方式
1. 串行队列同步执行任务
- 同步不具有开辟新线程的能力,不会开辟新的线程去执行任务,会在当前程序的主线程中执行任务。
- 按照串行的方式去执行任务
-(void)syncSERIAL{
NSLog(@"star");
//不会开辟新的线程
dispatch_queue_t queue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"SERIAL_work_1 ");
});
dispatch_sync(queue, ^{
NSLog(@"SERIAL_work_2 ");
});
dispatch_sync(queue, ^{
NSLog(@"SERIAL_work_3 ");
});
NSLog(@"end");
}
执行结果
2016-07-20 20:08:09.695 GCD_Demo[8196:1029563] star
2016-07-20 20:08:09.696 GCD_Demo[8196:1029563] SERIAL_work_1
2016-07-20 20:08:09.696 GCD_Demo[8196:1029563] SERIAL_work_2
2016-07-20 20:08:09.696 GCD_Demo[8196:1029563] SERIAL_work_3
2016-07-20 20:08:09.696 GCD_Demo[8196:1029563] end
由于是同步操作,不能开辟线程,所以都是在主线程并按照顺序执行
2. 串行队列异步执行任务
- 异步具有创建新线程的能力,会开辟新的线程去执行任务
- 按照串行的方式去执行任务
-(void)asyncSERIAL{
NSLog(@"star");
//会开辟新的线程,但是是串行执行任务
dispatch_queue_t queue=dispatch_queue_create("ki", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"SERIAL_work_1 ");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"SERIAL_work_2 ");
});
dispatch_async(queue, ^{
NSLog(@"SERIAL_work_3 ");
});
NSLog(@"end");
}
执行结果
2016-07-20 20:09:58.494 GCD_Demo[8213:1031268] star
2016-07-20 20:09:58.495 GCD_Demo[8213:1031268] end
2016-07-20 20:10:01.496 GCD_Demo[8213:1031315] SERIAL_work_1
2016-07-20 20:10:03.502 GCD_Demo[8213:1031315] SERIAL_work_2
2016-07-20 20:10:03.502 GCD_Demo[8213:1031315] SERIAL_work_3
因为是异步操作,所以有个编号为2的子线程被开辟,但有因为是串行队列,所以只开辟了一个线程。最终造就了三个任务顺序执行。
3. 并行队列同步执行任务
- 同步不具有创建新线程的能力,不会开辟新的线程去执行任务,会在当前程序的主线程去执行任务
- 按照同步的方式去执行任务
-(void)syncCONCURRENT{
NSLog(@"star");
//不会开辟新的线程
//串行执行命令
dispatch_queue_t queue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"CONCURRENT_work_1 ");
});
dispatch_sync(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"CONCURRENT_work_2 ");
});
dispatch_sync(queue, ^{
NSLog(@"CONCURRENT_work_3 ");
});
NSLog(@"end");
}
执行结果
2016-07-20 20:13:03.753 GCD_Demo[8232:1033759] star
2016-07-20 20:13:06.755 GCD_Demo[8232:1033759] CONCURRENT_work_1
2016-07-20 20:13:08.756 GCD_Demo[8232:1033759] CONCURRENT_work_2
2016-07-20 20:13:08.756 GCD_Demo[8232:1033759] CONCURRENT_work_3
2016-07-20 20:13:08.757 GCD_Demo[8232:1033759] end
虽然并行队列决定了该队列中可以有多个线程,但由于是同步操作,不能开辟线程,所以还都是在主线程中按顺序执行。
4. 并发队列异步执行任务(常用)
- 异步具有创建新线程的能力,会开辟新的线程去执行任务,不会在当前程序的主线程去执行任务
- 按照并发的方式去执行任务
-(void)asyncCONCURRENT{
NSLog(@"star");
//一个队列 为 每个任务开辟一个线程
dispatch_queue_t queue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"CONCURRENT_work_1 ");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"CONCURRENT_work_2 ");
});
dispatch_async(queue, ^{
NSLog(@"CONCURRENT_work_3 ");
});
NSLog(@"end");
}
执行结果
2016-07-20 20:18:26.768 GCD_Demo[8256:1038143] star
2016-07-20 20:18:26.768 GCD_Demo[8256:1038143] end
2016-07-20 20:18:26.769 GCD_Demo[8256:1038192] CONCURRENT_work_3
2016-07-20 20:18:28.771 GCD_Demo[8256:1038179] CONCURRENT_work_2
2016-07-20 20:18:29.773 GCD_Demo[8256:1038188] CONCURRENT_work_1
并行队列可以里可以有多个线程,同步执行的方式又可以开辟多个线程,所以这里实现了多个线程并行执行。
5.Dispatch Group
当我们想在gcd queue中所有的任务执行完毕之后做些特定事情的时候,也就是队列的同步问题,如果队列是串行的话,那将该操作最后添加到队列中即可,但如果队列是并行队列的话,这时候就可以利用dispatch_group来实现了,dispatch_group能很方便的解决同步的问题。dispatch_group_create可以创建一个group对象,然后可以添加block到该组里面,下面看下它的一些用法:
dispatch_group_notify
是通过异步的方式通知,所以,不会阻塞线程-(void)asyncGroupNotify { NSLog(@"star"); dispatch_group_t group=dispatch_group_create(); dispatch_queue_t queue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"group_work_1"); }); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:6]; NSLog(@"group_work_2"); }); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"group_work_3"); }); dispatch_group_notify(group, queue, ^{ NSLog(@"dispatch_group_Notify 结束"); }); }
运行结果
2016-07-21 13:51:40.600 GCD_Demo[9044:1162213] star
2016-07-21 13:51:41.605 GCD_Demo[9044:1162359] group_work_1
2016-07-21 13:51:42.608 GCD_Demo[9044:1162389] group_work_3
2016-07-21 13:51:46.603 GCD_Demo[9044:1162349] group_work_2
2016-07-21 13:51:46.605 GCD_Demo[9044:1162349] dispatch_group_Notify 结束
- dispatch_group_wait
会阻塞当前线程,知道任务都完成时才会继续执行下面的代码
-(void)asyncGroupWait
{
NSLog(@"star");
dispatch_group_t group=dispatch_group_create();
dispatch_queue_t queue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group_work_1");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:6];
NSLog(@"group_work_2");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"group_work_3");
});
//在此设置了一个12秒的等待时间,如果group的执行结束没有到12秒那么就返回0
//如果执行group的执行时间超过了12秒,那么返回非0 数值,
//在使用dispatch_group_wait函数的时候,会阻塞当前线程,阻塞的时间 在wait函数时间值和当前group执行时间值取最小的。
long kk=dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 12 * NSEC_PER_SEC));
if(kk==0)
{
NSLog(@"dispatch_group_wait 结果1");
}
else
{
NSLog(@"dispatch_group_wait 结果2");
}
}
执行结果
2016-07-21 13:56:47.471 GCD_Demo[9065:1165380] star
2016-07-21 13:56:48.472 GCD_Demo[9065:1165494] group_work_1
2016-07-21 13:56:49.476 GCD_Demo[9065:1165502] group_work_3
2016-07-21 13:56:53.475 GCD_Demo[9065:1165485] group_work_2
2016-07-21 13:56:53.475 GCD_Demo[9065:1165380] dispatch_group_wait 结果1
- dispatch_group_enter&dispatch_group_leave
假如我们不想使用dispatch_group_async异步的将任务丢到group中去执行,这时候就需要用到dispatch_group_enter跟dispatch_group_leave方法,这两个方法要配对出现,以下这两种方法是等价的:
-(void)asyncGroupEnter
{
// 群组-统一监控一组任务
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 1> 入组 -> 之后的 block 会被 group 监听
// dispatch_group_enter 一定和 dispatch_group_leave 要配对出现
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"dispatch_async_work1");
// block 的末尾,所有任务执行完毕后,添加一个出组
dispatch_group_leave(group);
});
// 再次入组
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:6];
NSLog(@"dispatch_async_work1");
// block 的末尾,所有任务执行完毕后,添加一个出组
dispatch_group_leave(group);
});
// 群组结束
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"OVER");
});
NSLog(@"come here");
}
执行结果
2016-07-21 15:21:40.707 GCD_Demo[9256:1205427] come here
2016-07-21 15:21:40.707 GCD_Demo[9256:1205465] dispatch_async_work1
2016-07-21 15:21:46.709 GCD_Demo[9256:1205460] dispatch_async_work1
2016-07-21 15:21:46.710 GCD_Demo[9256:1205427] OVER
6. Dispatch Block
添加到gcd队列中执行的任务是以block的形式添加的,block封装了需要执行功能,block带来的开发效率提升就不说了,gcd跟block可以说是一对好基友,能够很好的配合使用。
-(void)dispatchBlock
{
dispatch_queue_t queue = dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block=dispatch_block_create(0, ^{
NSLog(@"dispatchBlock_work");
});
dispatch_sync(queue, block);
}
1.dispatch_block_wait
当需要等待前面的任务执行完毕时,我们可以使用dispatch_block_wait这个接口,设置等待时间DISPATCH_TIME_FOREVER会一直等待直到前面的任务完成.用法跟dispatch_group_wait类似
-(void)dispatchBlockWait
{
dispatch_queue_t queue = dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"before sleep");
[NSThread sleepForTimeInterval:6];
NSLog(@"after sleep");
});
dispatch_async(queue, block);
//等待前面的任务执行完毕
long kk=dispatch_block_wait(block, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
if(kk==0)
{
NSLog(@"coutinue");
}
else
{
NSLog(@"timeOut!!!");
}
}
执行结果
2016-07-21 16:28:38.313 GCD_Demo[9533:1251011] before sleep
2016-07-21 16:28:41.314 GCD_Demo[9533:1250971] timeOut!!!
2016-07-21 16:28:44.318 GCD_Demo[9533:1251011] after sleep
2.dispatch_block_notify
dispatch_block_notify当观察的某个block执行结束之后立刻通知提交另一特定的block到指定的queue中执行,该函数有三个参数,第一参数是需要观察的block,第二个参数是被通知block提交执行的queue,第三参数是当需要被通知执行的block
-(void)dispatchBlockNotify
{
dispatch_queue_t queue = dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_SERIAL);
dispatch_block_t previousBlock = dispatch_block_create(0, ^{
NSLog(@"previousBlock begin");
[NSThread sleepForTimeInterval:2];
NSLog(@"previousBlock done");
});
dispatch_async(queue, previousBlock);
dispatch_block_t notifyBlock = dispatch_block_create(0, ^{
NSLog(@"notifyBlock");
});
//当previousBlock执行完毕后,提交notifyBlock到global queue中执行
dispatch_block_notify(previousBlock, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), notifyBlock);
}
执行结果
2016-07-21 16:38:19.756 GCD_Demo[9664:1261328] previousBlock begin
2016-07-21 16:38:21.762 GCD_Demo[9664:1261328] previousBlock done
2016-07-21 16:38:21.762 GCD_Demo[9664:1261329] notifyBlock
3. dispatch_block_cancel
可以取消提交到队列的block
-(void)dispatchBlockCancel
{
dispatch_queue_t queue = dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block1 = dispatch_block_create(0, ^{
NSLog(@"block1 begin");
[NSThread sleepForTimeInterval:1];
NSLog(@"block1 done");
});
dispatch_block_t block2 = dispatch_block_create(0, ^{
NSLog(@"block2 ");
});
dispatch_async(queue, block1);
dispatch_async(queue, block2);
dispatch_block_cancel(block2);
}
执行结果
2016-07-21 16:50:28.140 GCD_Demo[9723:1272259] block1 begin
2016-07-21 16:50:29.144 GCD_Demo[9723:1272259] block1 done
7.dispatch_after
来延迟执行的GCD方法,因为在主线程中我们不能用sleep来延迟方法的调用,所以用它是最合适的,我们做一个简单的例子:
执行结果
2016-07-21 16:12:07.204 GCD_Demo[9439:1237251] dispatchAfter_star
2016-07-21 16:12:07.211 GCD_Demo[9439:1237251] dispatchAfter_work
2016-07-21 16:12:09.398 GCD_Demo[9439:1237251] dispatchAfter_work
2016-07-21 16:12:11.205 GCD_Demo[9439:1237251] dispatchAfter_work
2016-07-21 16:12:13.205 GCD_Demo[9439:1237251] dispatchAfter_work
2016-07-21 16:12:15.205 GCD_Demo[9439:1237251] dispatchAfter_work
8.dispatch_apply
dispatch_apply类似一个for循环,会在指定的dispatch queue中运行block任务n次,如果队列是并发队列,则会并发执行block任务,dispatch_apply是一个同步调用,block任务执行n次后才返回。
-(void)dispatchApply
{
dispatch_queue_t queue = dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(6, queue, ^(size_t i) {
NSLog(@"do a job %zu times",i);
});
NSLog(@"go on");
}
执行结果
2016-07-21 17:04:54.558 GCD_Demo[9831:1284549] do a job 0 times
2016-07-21 17:04:54.564 GCD_Demo[9831:1284583] do a job 1 times
2016-07-21 17:04:54.564 GCD_Demo[9831:1284589] do a job 2 times
2016-07-21 17:04:54.564 GCD_Demo[9831:1284593] do a job 3 times
2016-07-21 17:04:54.564 GCD_Demo[9831:1284549] do a job 4 times
2016-07-21 17:04:54.564 GCD_Demo[9831:1284583] do a job 5 times
2016-07-21 17:04:54.566 GCD_Demo[9831:1284549] go on
9.dispatch_once
整个程序运行中只会执行一次,使用dispatch_once可以简化代码并且彻底保证线程安全,开发者根本无须担心加锁或者同步。所有问题都由GCD在底层处理。由于每次调用时都必须使用完全相同的标记,所以标记要声明成static。所以用在单例模式上是最好的
static SingletonTimer * instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[SingletonTimer alloc] init];
});
return instance;
10. dispatch_barrier_async
dispatch_barrier_async用于等待前面的任务执行完毕后自己才执行,而它后面的任务需等待它完成之后才执行。一个典型的例子就是数据的读写,通常为了防止文件读写导致冲突,我们会创建一个串行的队列,所有的文件操作都是通过这个队列来执行,比如FMDB,这样就可以避免读写冲突。不过其实这样效率是有提升的空间的,当没有更新数据时,读操作其实是可以并行进行的,而写操作需要串行的执行
-(void)diapatchBarrier
{
dispatch_queue_t queue = dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:6];
NSLog(@"dispatch_async_work1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"dispatch_async_work2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_async_work3");
[NSThread sleepForTimeInterval:1];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async_work4");
});
}
执行结果
2016-07-21 15:54:24.402 GCD_Demo[9354:1225160] dispatch_async_work2
2016-07-21 15:54:28.403 GCD_Demo[9354:1225152] dispatch_async_work1
2016-07-21 15:54:28.403 GCD_Demo[9354:1225152] dispatch_async_work3
2016-07-21 15:54:30.412 GCD_Demo[9354:1225152] dispatch_async_work4
11.dispatch_set_target_queue
1.系统的Global Queue是可以指定优先级的,那我们可以用到dispatch_set_target_queue这个方法来指定自己创建队列的优先级
-(void)DispatchSet
{
dispatch_queue_t serialDiapatchQueue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t dispatchgetglobalqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_set_target_queue(serialDiapatchQueue, dispatchgetglobalqueue);
dispatch_async(serialDiapatchQueue, ^{
NSLog(@"我优先级低,先让让");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"我优先级高,我先block");
});
}
执行结果
2016-07-21 17:22:02.512 GCD_Demo[9902:1297023] 我优先级高,我先block
2016-07-21 17:22:02.512 GCD_Demo[9902:1297035] 我优先级低,先让让
2.dispatch_set_target_queue除了能用来设置队列的优先级之外,还能够创建队列的层次体系,当我们想让不同队列中的任务同步的执行时,我们可以创建一个串行队列,然后将这些队列的target指向新创建的队列即可
-(void)dispatchSet2
{
dispatch_queue_t targetQueue = dispatch_queue_create("target_queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_async(queue1, ^{
[NSThread sleepForTimeInterval:3.f];
NSLog(@"do job1");
});
dispatch_async(queue2, ^{
[NSThread sleepForTimeInterval:2.f];
NSLog(@"do job2");
});
dispatch_async(queue2, ^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"do job3");
});
}
执行结果
2016-07-21 17:28:54.327 GCD_Demo[10043:1303853] do job1
2016-07-21 17:28:56.331 GCD_Demo[10043:1303853] do job2
2016-07-21 17:28:57.335 GCD_Demo[10043:1303853] do job3
12.dispatch_semaphore_t
信号量是一种老式的线程概念,由非常谦卑的 Edsger W. Dijkstra 介绍给世界。信号量之所以比较复杂是因为它建立在操作系统的复杂性之上。
- (void)downloadImageURLWithString:(NSString *)URLString
{
// 1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURL *url = [NSURL URLWithString:URLString];
__unused Photo *photo = [[Photo alloc]
initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *error) {
if (error) {
XCTFail(@"%@ failed. %@", URLString, error);
}
// 2
dispatch_semaphore_signal(semaphore);
}];
// 3
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, kDefaultTimeoutLengthInNanoSeconds);
if (dispatch_semaphore_wait(semaphore, timeoutTime)) {
XCTFail(@"%@ timed out", URLString);
}
}
下面来说明你代码中的信号量是如何工作的:
- 创建一个信号量。参数指定信号量的起始值。这个数字是你可以访问的信号量,不需要有人先去增加它的数量。(注意到增加信号量也被叫做发射信号量)。译者注:这里初始化为0,也就是说,有人想使用信号量必然会被阻塞,直到有人增加信号量。
- 在 Completion Block 里你告诉信号量你不再需要资源了。这就会增加信号量的计数并告知其他想使用此资源的线程。
- 这会在超时之前等待信号量。这个调用阻塞了当前线程直到信号量被发射。这个函数的一个非零返回值表示到达超时了。在这个例子里,测试将会失败因为它以为网络请求不会超过 10 秒钟就会返回——一个平衡点!
13.GCD定时器
基于以前讲的runloop中的CFRunLoopTimerRef :
- CFRunLoopTimerRef是基于时间的触发器
- CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode的影响(Tracking,Defalult)
- GCD的定时器不受RunLoop中Mode的影响(RunLoop内部也是基于GCD实现的,可以根据源码看到), 比如滚动TableView的时候,GCD的定时器不受影响
- 一般NSTimer不是特别准, NSTimer是在RunLoop中, RunLoop要处理各种东西(source,timer,observe),有时导致NSTimer不是特别准
GCD定时器算是一个源(source),类型是Timer
-(void)startTime{
__block int timeout=30; //倒计时时间
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
if(timeout=0){ //倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//倒计时时间结束,回到主线程 根据自己需求设置
});
}else{
// int minutes = timeout / 60;
int seconds = timeout % 60;
NSString *strTime = [NSString stringWithFormat:@"%.2d", seconds];
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置
NSLog(@"____%@",strTime);
[l_timeButton setTitle:[NSString stringWithFormat:@"%@秒后重新发送",strTime] forState:UIControlStateNormal];
l_timeButton.userInteractionEnabled = NO;
});
timeout--;
}
});
dispatch_resume(_timer);
}